I have narrowed down a 33,364 entry XML file to the 1,068 that I need. Now I am attempting to gather pieces of information from each node that I have narrowed my search down to, and store each piece of information in a hash, so that I can list out the relevant data in a rails view.
Here is the code in my controller (home_controller.rb) --
class HomeController < ApplicationController
# REQUIRE LIBRARIES
require 'nokogiri'
require 'open-uri'
def search
end
def listing
#properties = {}
# OPEN THE XML FILE
mits_feed = File.open("app/assets/xml/mits.xml")
# OUTPUT THE XML DOCUMENT
doc = Nokogiri::XML(mits_feed)
doc.xpath("//Property/PropertyID/Identification[#OrganizationName='northsteppe']").each do |property|
# GATHER PROPERTY INFORMATION
information = {
"street_address" => property.xpath("Address/AddressLine1").text,
"city" => property.xpath("Address/City").text,
"zipcode" => property.xpath("Address/PostalCode").text,
"short_description" => property.xpath("Information/ShortDescription").text,
"long_description" => property.xpath("Information/LongDescription").text,
"rent" => property.xpath("Information/Rents/StandardRent").text,
"application_fee" => property.xpath("Fee/ApplicationFee").text,
"bedrooms" => property.xpath("Floorplan/Room[#RoomType='Bedroom']/Count").text,
"bathrooms" => property.xpath("Floorplan/Room[#RoomType='Bathroom']/Count").text,
"bathrooms" => property.xpath("ILS_Unit/Availability/VacancyClass").text
}
# MERGE NEW PROPERTY INFORMATION TO THE EXISTING HASH
#properties.merge(information)
end
end
end
I'm not getting any errors and my view is loading fine, but it is pulling up blank. Here is my view file (listing.html.erb) --
<div class="propertiesHolder">
<% if #properties %>
<ul>
<% #properties.each do |property| %>
<li><%= property.information.street_address %></li>
<% end %>
</ul>
<% else %>
<h1>There are no properties that match your search</h1>
<% end %>
</div>
Does anyone know why this might be pulling up blank? I would assume that I would receive an error if I had done something incorrect in the code. I also tried just outputting "Hello World" as text for each |property| and this also pulled up blank. Thank you!
Ruby merge does not mutate your hash. It just returns the two hashes as one.
Example
h1 = { "a" => 100, "b" => 200 }
h2 = { "b" => 254, "c" => 300 }
h1.merge(h2)
#=> {"a"=>100, "b"=>254, "c"=>300}
h1
#=> {"a"=>100, "b"=>200}
Note how h1 still retains its original values?
What you will want to do is rename your information hash to #properties. I suggest this because you are merging a hash with information in it (information) with an empty hash (#properties). So instead of overwriting when you merge the hashes, just use the first hash.
Related
I'm trying to create a simple two-page web-app that allows users to search for words, then display their definitions on a new page.
The problem is that when searching for a word, I'm not getting any response back from my model file.
I think the problem is trying to convert the JSON request to a hash, then accessing/displaying the hash in the view (not sure how to go about this).
app/views/index.html.erb
<%= form_tag("/search", method: "get") do %>
<%= text_field_tag :search, params[:search], :class => "search-form" %>
<%= submit_tag nil, :class => "search-glass", :src => "search-icon.png", :type => "image" %>
<% end %>
app/controllers/words_controller.rb
def search
#word = Word.search(params[:search])
render layout: "application", template: "words/index"
end
app/models/word.rb
class Word
require 'rubygems'
require 'json'
attr_accessor :search
def self.search(search)
# Get back JSON response
response = Wordnik.word.get_definitions(search)
# Parse JSON to an array of hashes?
hash_result = JSON.parse(response)
end
end
When searching for a single word, I'm getting the following error (screenshot):
Any help is greatly appreciated.
When you call Wordnik.word.get_definitions(search), an Array of Hashes is returned, not a JSON string. Remove the line:
hash_result = JSON.parse(response)
Also having your API key publicly available on Github is probably not a good idea.
I have a company model which can have many tags. It works fine, but in one occasion it does not work. The occasion is when company model validation fails. After :render => 'edit' it does not show tags in the view. I suspect the data-pre is not taking the data correctly. I would also like for tags to be preserved when solving validations.
I got this idea from here: http://railscasts.com/episodes/167-more-on-virtual-attributes
I use Input token control: http://loopj.com/jquery-tokeninput/
This is what I have in Company model regarding the tag_tokens:
before_save :save_tag_tokens
attr_writer :tag_tokens
attr_accessible :tag_tokens
def tag_tokens
#tag_tokens || tags.to_json(:only => [:id, :name])
end
def save_tag_tokens
if #tag_tokens
#tag_tokens.gsub!(/CREATE_(.+?)_END/) do
Tag.create!(:name => $1.strip.downcase).id
end
self.tag_ids = #tag_tokens.split(",")
end
end
Here is the code from the view:
<div class="input text no-border">
<% Tag.include_root_in_json = false %>
<%= company_form.label :tag_tokens, t('form.account.company.edit.company_tags_html')%>
<%= company_form.text_field :tag_tokens, :id => 'company_tag_tokens', "data-pre" => #company.tag_tokens%>
<p class="tip"><%= t('form.account.company.edit.tag_tip') %></p>
</div>
EDIT:
OK, so I see what is the problem with the above code.
When i load edit page data-pre contains this: data-pre="[{"id":1704,"name":"dump truck"}]". when I submit the form with validation error the data-pre contains: data-pre="1704".
if i change the code to this:
def tag_tokens
tags.to_json(:only => [:id, :name])
end
new tags that were not yet save to the company model are removed, because they are read from the DB everytime. How can I preserve the entered data between form transitions?
OK, I've written a solution, it might not be the nicest one, but it works to me! It parses the input token value to JSON format (when validation fails), which is used when loading the page. Under page load it just loads tags from DB.
def tag_tokens
if #tag_tokens
#if there is user info, parse it to json format. create an array
array = #tag_tokens.split(",")
tokens_json = []
#loop through each tag and check if it's new or existing
array.each do |tag|
if tag.to_s.match(/^CREATE_/)
#if new generate json part like this:
tag.gsub!(/CREATE_(.+?)_END/) do
tokens_json << "{\"id\":\"CREATE_#{$1.strip.downcase}_END\",\"name\":\"Add: #{$1.strip.downcase}\"}"
end
else
#if tag is already in db, generate json part like this:
tokens_json << "{\"id\":#{tag},\"name\":\"#{Tag.find_by_id(tag).name}\"}"
end
end
#encapsulate the value for token input with [] and add all tags from array
"[#{tokens_json.to_sentence(:last_word_connector => ',', :words_connector => ',', :two_words_connector => ',')}]"
else
#if there is no user input already load from DB
tags.to_json(:only => [:id, :name])
end
end
I need to export a previously rendered table from my view to pdf. When I build the array of hashes as follows:
__index = 0
#people.each do |p| %>
#pdfdata[__index] = {
[:name] => p.name.to_s,
[:surname] => p.surname.to_s
__index += 1
end
end
and send it to the controller in order to export it on a pdf as follows:
hidden_field_tag(:pdfdata, #pdfdata)
when I get the params[:pdfdata] I cannot find a way unless I build a string parser to map the data accordingly... is there a better way to do this?
Modifying your code a little bit to get
#people.each_with_index do |p,i| %>
#pdfdata[i] = {
[:name] => p.name.to_s,
[:surname] => p.surname.to_s}
end
and use this gem to create the hidden has field
https://github.com/brianhempel/hash_to_hidden_fields
I'm using a select field in a Rails app that is NOT tied to a related model, but stores integer values for a static series of options , i.e.,
<%= select (:this_model, :this_field, [['Option1',1],['Option2',2],['Option3',3],['Option4',4]] ) %>
In a show/ index view, if I want to display the option text (i.e. Option1, Option2, etc) rather than the integer value stored in the database, how do I achieve this?
Thanks for helping a noob learn the ropes!
EDIT
Based on Thorsten's suggestion below, I implemented the following. But it is returning nil, and I can't figure out why.
Invoice model:
##payment_status_data = { 1 => "Pending Invoice" , 2 => "Invoiced" , 3 => "Deposit Received", 4 => "Paid in Full"}
def text_for_payment_status
##payment_status_data[payment_status]
end
Invoice show view:
Payment Status: <%= #invoice.text_for_payment_status %>
In the console:
irb > i=Invoice.find(4)
=> [#<Invoice id: 4, payment_status: 1 >]
irb > i.text_for_payment_status
=> nil
I've tried defining the hash with and without quotes around the keys. What am I missing?
something like this would work:
<%= form_for #my_model_object do |form| %>
<%= form.label :column_name "Some Description" %>
<%= form.select :field_that_stores_id, options_for_select({"text1" => "key1", "text 2" => "key2"}) %>
<% end %>
Update
If you later want to display the text you can get it from a simple hash like this:
{"key1" => "text 1", "key2" => "text2"}[#my_object.field_that_stores_id]
But you better store this hash somewhere in a central place like the model.
class MyModel < ActiveRecord
##my_select_something_data = {"key1" => "text 1", "key2" => "text2"}
def text_for_something_selectable
##my_select_something_data[field_that_stores_id]
end
end
Then you can use it in your views like
#my_object.text_for_something_selectable
There are many possible variations of this. But this should work and you would have all information in a central place.
Update
Ok, I used something similar for our website. We need to store return_headers for rma. Those need to store a return reason as a code. Those codes are defined in an external MS SQL Server Database (with which the website exchanges lots of data, like orders, products, and much more). In the external db table are much more return reasons stored than I actually need, so I just took out a few of them. Still must make sure, the codes are correct.
So here goes he model:
class ReturnHeader < AciveRecord::Base
##return_reason_keys = {"010" => "Wrong Produc",
"DAM" => "Damaged",
"AMT" => "Wrong Amount"}
def self.return_reason_select
##return_reason_keys.invert
end
def return_reason
##return_reason_keys[nav_return_reason_code]
end
end
Model contains more code of course, but that's the part that matters. Relevant here is, that keys in the hash are strings, not symbols.
In the views i use it like this:
In the form for edit:
<%= form_for #return_header do |form| %>
<%= form.label :nav_return_reason_code "Return Reason" %>
<%= form.select :nav_return_reason_code, options_for_select(ReturnHeader.return_reason_select, #return_header.nav_return_reason_code) %>
<% end %>
(Maybe no the most elegant way to do it, but works. Don't know, why options_for_select expects a hash to be "text" => "key", but that's the reason, why above class level method returns the hash inverted.)
In my index action the return reason is listed in one of the columns. There I can get the value simply by
#return_headers.each do |rh|
rh.return_reason
end
If you have trouble to get it run, check that keys a correct type and value. Maybe add some debug info with logger.info in the methods to see what actual data is used there.
I am trying to pass some filters in my params through a form like so:
hidden_field_tag "filters", params[:filters]
For some reason the params get changed in the next page. For example, if params[:filters] used to be...
"filters"=>{"name_like_any"=>["apple"]} [1]
...it gets changed to...
"filters"=>"{\"name_like_any\"=>[\"apple\"]}" [2]
note the extra quotations and backslashes in [2] when compared to [1].
Any ideas? I'm attempting to use this with searchlogic for some filtering, but I need it to persist when I change change objects in forms. I would prefer not to have to store it in session.
My solution was just to re-create each of param with key-value pair:
<% params[:filters].each do |key,value| %>
<%= hidden_field_tag "filters[#{key}]",value %>
<% end %>
You actually want/need to 'serialize' a hash using hidden fields.
Add this to your ApplicationHelper :
def flatten_hash(hash = params, ancestor_names = [])
flat_hash = {}
hash.each do |k, v|
names = Array.new(ancestor_names)
names << k
if v.is_a?(Hash)
flat_hash.merge!(flatten_hash(v, names))
else
key = flat_hash_key(names)
key += "[]" if v.is_a?(Array)
flat_hash[key] = v
end
end
flat_hash
end
def flat_hash_key(names)
names = Array.new(names)
name = names.shift.to_s.dup
names.each do |n|
name << "[#{n}]"
end
name
end
def hash_as_hidden_fields(hash = params)
hidden_fields = []
flatten_hash(hash).each do |name, value|
value = [value] if !value.is_a?(Array)
value.each do |v|
hidden_fields << hidden_field_tag(name, v.to_s, :id => nil)
end
end
hidden_fields.join("\n")
end
Then, in view:
<%= hash_as_hidden_fields(:filter => params[:filter]) %>
This should do the trick, even if you have a multilevel hash/array in your filters.
Solution taken http://marklunds.com/articles/one/314
I just wrote a gem to do this called HashToHiddenFields.
The core of the gem is this code:
def hash_to_hidden_fields(hash)
query_string = Rack::Utils.build_nested_query(hash)
pairs = query_string.split(Rack::Utils::DEFAULT_SEP)
tags = pairs.map do |pair|
key, value = pair.split('=', 2).map { |str| Rack::Utils.unescape(str) }
hidden_field_tag(key, value)
end
tags.join("\n").html_safe
end
Here's how I managed to pass a parameter value through my view - that is, from View A through View B and on to the controller:
In View A (index):
<%= link_to 'LinkName', {:action => "run_script", :id => object.id} %>
In View B (run_script):
<%= form_tag :action => 'index', :id => #object %>
<%= hidden_field_tag(:param_name, params[:id]) %>
In the controller:
Just reference params[:param_name] to make use of the value.
The key transition that wasn't documented anywhere I could find is where {... :id => object.id} from View A is passed on to View B as <%... :id => #object %>, which View B then passes on to the controller as (:param_name, params[:id]) through the hidden_field_tag construct.
I didn't see this documented anywhere but after perusing several posts across several sites including this post (whose syntax provided the key inspiration), the solution finally gelled. I've seen the caveats on hidden fields pertaining to security but have found no other way to do this given my current design, such as it is.
it's because when you convert in HTML with your hidden_field_tag, the backquote is add. After when you received it like a string not a Hash.
The Hash type can't exist in HTML. You have only string. So if you want pass your hash (not recommend by me), you need eval it when you received it. But can be a big security issue on your application.
As a caveat to Vlad's answer, I had to use raw:
<%= raw hash_as_hidden_fields(:filter => params[:filter]) %>
to get it to work in Rails 3.1.1. Essentially, the text being output was being escaped, eg., "<" becoming "<".
Assuming the hash is strings, symbols, numbers, and arrays, you can call eval to convert the params string of the hash from the hidden_fields form back into a hash in the controller. Then the backslash escape characters for the quotes added are no longer an issue:
hash = eval(params["hash_string"].to_s)
Credit to the following article for helping identify this simple solution for my case:
How do I convert a String object into a Hash object?
Keep in mind the contents of the params should be cleaned with .require and .permit.