ERB is not getting some values of a local hash - ruby

I am using Sinatra and rendering views with ERB.
I have the following action
get '/user/:id' do
u = #users.retrieve( params[:id] )
u[:mykey] = [1,2,3]
erb( :user, :locals => { :user => u } )
end
and the view looks like this
<body>
<h1><%= user["name"] %></h1>
<pre><%= user["mykey"].to_json %></pre>
and where I expect to get the [1,2,3] array, I get a big fat null.
Primitive values such as the name, are passed without a problem.

Is this of class Hash or HashWithIndifferentAccess?
You are setting user[:mykey] and retrieving user["mykey"]. Use symbol or string, don't mix them unless you are using HashWithIndifferentAccess.

Related

ruby on rails, search results to json array (jbuilder)

I'm new to rails and I just have a quick question...
I have a calendar gem which currently pulls event data from json as such (viewings/index.json.jbuilder)
json.array!(#viewings) do |viewing|
json.extract! viewing, :id, :room_id, :user_id, :start_time, :end_time, :notes
json.title viewing.user.name
json.start viewing.start_time
json.end viewing.end_time
json.url viewing_url(viewing, format: :html)
end
anyway, I have created a search feature on my "viewings" index page, where a user selects the room from a drop-down and it displays the corresponding viewings.
However, I need to make a JSON array from the results of the search, so it can be fed to the calendar.
I know I can achieve this by doing something like, which does work:
#viewings = Viewing.where(room_id: 1 )
but obviously I want to pass a variable in to that line of code, something like
#viewings = Viewing.where(room_id: params[:search_string] )
The search string will always be a room ID, but the above code (and any variations I can think of) don't seem to work.
OR can I make a JSON array from my search output. my search is as so...
def search
#rooms = Room.all
#viewings = Viewing.simple_search(params[:search_string])
render :action => "index"
end
Can anyone help?!
EDIT:
I'm still struggling with this one a lot. I had a look at the jquery page below but its still not doing what I hoped.
I have simplified the function in my viewings controller as so:
def rmselect
#query1 = params[:rmno]
#rooms = Room.all
#viewings=Room.find(params[:rmno]).viewings
render :action => "index"
end
And I have created an _rmselect.json.jbuilder as so:
#viewings = Viewing.where(room_id: #query1 )
json.array!(#viewings) do |viewing|
json.title viewing.user.name
json.start viewing.start_time
json.end viewing.end_time
end
I was hoping the #query1 variable would be passed into the jbuilder page which would cause it only to display viewings for the selected room. However, it seems that jbuilder cannot see any variables unless they are in def index (where it's no good).
I also can't understand why _rmselect.json.jbuilder is taking the #viewings.all from def index, rather then the #viewings=Room.find(params[:rmno]).viewings from def rmselect.
Ok, I suppose the situation is like that:
Viewing model:
class Viewing < ActiveRecord::Base
...
belongs_to :room
...
end
Room model:
class Room < ActiveRecord::Base
...
has_many :viewings
...
end
Retrive viewings from room id, you can do something like:
#viewings=Room.find(params[:search_string]).viewings
For create JSON array of result, render jbuilder view who contains something like that:
# #people = People.all
json.array! #people, :id, :name
# => [ { "id": 1, "name": "David" }, { "id": 2, "name": "Jamie" } ]
I report an example of official jbuilder docs I think you can use json.array with viewings:
json.array! #viewings, field1, field2, ...
Otherwise you can use this in your controller:
render json: #viewings
but if you've used jbuilder, I think it's better you continue that way.

Sinatra haml Select and Delete several files

I'm trying to delete several files based on a list but I'm having problems getting the params from the chekcbox's
This is my list.haml:
%form(method="post" action="/selection" enctype="multipart/form-data")
- #files.each do |file|
%br
%input{:type => "checkbox", :name => "checkbox[]", :value => "#{file}" }
=file
%br
%input(type='submit' value="Delete Selected Files")
Now, for now I was just trying to see what I get in params, so I can later deal with how to delete this list of files.
params.inspect
"Gives me" ≃> {"checkbox"=>["yet_another_file.txt", "file1", "file2"]}
But I can't figure out how do I put this into an Array so I can do something like
var.each do |c|
puts c
end
I tried var = params[:checkbox] but var is empty, does anyone now how can I do this?
Thanks
You should use var = params["checkbox"], since the params key is not a symbol, but a string.

Trying to populate gmaps4rails with multiple json strings in one page

I hope I am asking this right, so please let me know if I'm way off.
The problem is trying to build a homepage that draws from multiple controllers, to display the nearest locations from multiple controllers, ie. food, businesses, ect.
Right now the individual listings pages have maps drawn from their respective
#json = Controller.all.to_gmaps4rails
How would I do something like :
#json = Controller1 Controller2 .all.to_gmaps4rails
I hope this isnt a noob question and I'm just having a bad day. Thanks guys!
edit 12.5.2011 #seanhill - this is one of the models, the other sections are very close to this format. First off, I wasn't even sure if my homepage requires it's own model, as it doesn't interact with the db at all, more pulling data from controllers that do the work. Thanks for the response Sean!
class Dining < ActiveRecord::Base
validates_uniqueness_of :name, :message => "already exists"
attr_accessible :name, :address, :cuisine, :latitude, :longitude, :about, :facebook, :twitter, :phone, :website
geocoded_by :address
after_validation :geocode, :if => :address_changed?
acts_as_gmappable :process_geocoding => false
def gmaps4rails_address
"#{self.address}"
end
def gmaps4rails_infowindow
"<h3>#{self.name}</h3><br /><h5>#{self.cuisine}</h5>"
end
def self.search(search)
if search
where('name LIKE ?', "%#{search}%")
else
scoped
end
end
end
Try this
holder = Controller1.all
holder << Controller2.all
#json = holder.flatten.map{|h| {lng: h.longitude, lat: h.latitude, class: h.class.to_s}}.to_json
Make sure to change longitude and latitude based on your column names and use js to manipulate the markers based upon class.
As the #Sean Hill said you shouldn't be calling .all on controllers but I think you have a slight misunderstanding of how things are working. Assuming you have a Model called Dining and another called Shop, when you call Dining.all or Shop.all inside class DiningsController < ApplicationController, you are calling .all on either the Dining Model or the Shop Model not on the DiningsController.
The information you display through a controller is only limited by the methods you call in it although it is best practice ensure the main focus of the information displayed is related to the respective controller.
So what you are really trying to do is get the records from multiple models and group them together to display them in a single map.
With that said the answer should read something like this
holder = Dining.all # Takes all Dining records returned as an array and sets them to holder variable
holder << Shop.all # Pushes the Shop records array into the holder with the dining records
holder.flatten!# Next we flatten the array so we only have a single array.
# Then we use the map method to run the given code one time for each instance
# in the holder array to extract the info we need. The results for every instance
# in holder are returned in an array which we then convert to_json.
#json = holder.map{|h| {lng: h.longitude, lat: h.latitude, class: h.class.to_s}}.to_json
#json1 = something.to_gmaps4rails
#json2 = something.to_gmaps4rails
#json = (JSON.parse(#json1) + JSON.parse(#json2)).to_json
I populated the map with my initial data of festivals, and then added the rides to it with javascript with this code,
<% content_for :scripts do %>
<script type="text/javascript">
Gmaps.map.callback = function() {
$.getJSON('/rides_gmap', function(data){
Gmaps.map.addMarkers(data);
});
}
</script>
<%end%>
In the rides controller I had this
def rides_gmap
#rides = Ride.all
#json = #rides.to_gmaps4rails do |ride, marker|
marker.infowindow render_to_string(:partial => "/rides/infowindow", :locals => { :ride => ride})
marker.picture({
'picture' => view_context.image_path("orange-dot.png"),
'width' => 20,
'height' => 20
})
marker.title "#{ride.address}"
marker.json({:ride_id => ride.id, :ride_festivaltype => ride.festival.festivaltype
end
respond_with #json
end
I hope this helps.

Is sprintf incompatible with sinatra?

Say I have this:
class Account
...
property :charge, Decimal, :precision => 7, :scale => 2
...
classy stuff
...
def self.balance(prefix)
x = Account.get(prefix.to_sym).order(:fields => [:charge]).sum(:charge)
sprintf("%5.2f", x)
end
end
(Edit: The value of all :charge fields is 0.13E2 (0.1E2 + 0.3E1). This is correctly returned. Only in a View does it seem to get borked from sprintf)
In IRB Account.balance(:AAA) returns => "13.00"
if I call Account.balance(:AAA) from a view I get TypeError at /accounts
can't convert nil into Float
Account.balance(:AAA) works anywhere I call it except in a view. If I remove sprintf("%5.2f", x) I get 0.13E2 in my view. (using Account.balance(:AAA).to_f in a view gives me 13.0)
Is sinatra incompatible with sprintf? or am I not understanding how to use sprintf?
(Edit: This is the offending view:)
<section>
<% #accounts.each do |account| %>
<article>
<h2><%= account.prefix %></h2>
<span><p>This account belongs to <%= account.name %> & has a balance of $<%= Account.balance(account.prefix) %>.</p></span>
</article>
<% end %>
</section>
Wouldn't it make more sense to define balance as an instance method rather than a class method? It looks from your example like you're calling balance in an account-specific way anyway, so why not make it:
# the model
class Account
#...
def balance
amount = self.order(:fields => [:charge]).sum(:charge)
sprintf "%5.2f", amount
# or the infix version:
"%5.2f" % amount
end
end
,
# the view
...balance of $<%= account.balance %>...
I know that this doesn't address sprintf per se, but the problem is more likely to be coming from the slightly convoluted lookup than from a built-in method. Even if my specific code doesn't suit your application, it might be worth simplifying the lookup step, even if that involves a few more lines of code.
The advantage of this approach is that there is no doubt that you'll be getting the right Account record.
tested it with a little sinatra app and it worked for me
app.rb
#!/usr/bin/env ruby
require 'rubygems'
require 'sinatra'
get '/' do
#x = 10.23
erb :index
end
views/index.erb
<%= sprintf("%5.2f", #x) %>
output:
10.23
ruby 1.9.2 / sinatra 1.3.1
I think there is another error before the sprintf because of your error message:
can't convert nil into Float
seems like your x is nil. try to be sure that x is not nil there, then sprintf should work as expected.

Array of Ruby objects returning strings on each method. Why?

Useful additional info: I am using the decent_exposure gem so this might be the issue - correcting the code below:
expose(:get_filter_tags) do
if params[:filter_tag_names]
filter_tag_names = Array(params[:filter_tag_names].split(" "))
filter_tags = Array.new
filter_tag_names.each do |f|
t = Tag.find_by_name(f)
filter_tags << t
end
end
end
So, something funny happens when I call this in the view:
query string ?utf8=✓&filter_tag_names=test
<% get_filter_tags.each do |ft| %>
<%= ft.name %>
<% end %>
Error message: undefined method `name' for "test":String
Why is this trying to call name on a string not a Tag object? If I put the following in the view, and have jut one filter_tag_names item
def getfiltertag
Tag.find_by_name(params[:filter_tag_names])
end
#view
<%= getfiltertag.name %>
query string: ?utf8=✓&filter=test
like above then I can call name just fine, so obviously I am doing something wrong to get an array of strings instead of objects. I just don't know what. Any suggestions?
Your problem is that each returns self — so if you write filter_tag_names.each, it returns filter_tag_names. You could fix this by explicitly returning filter_tags, but more idiomatically, you could just rewrite it as:
expose(:get_filter_tags) do
if params[:filter_tag_names]
filter_tag_names = Array(params[:filter_tag_names].split(" "))
filter_tag_names.map {|f| Tag.find_by_name(f) }
end
end
Just as an aside, this method will return nil if there aren't any filter tag names. You may want to do that, or you might want to return an empty collection to avoid exceptions in the calling code.

Resources