I have this code:
Model:
class WeatherLookup
attr_accessor :temperature, :icon, :condition, :zip, :fcttext
def fetch_weather(city)
HTTParty.get("http://api.wunderground.com/api/api_key/forecast/lang:NL/q/IT/#{city.slug}.xml")
end
def initialize
weather_hash = fetch_weather
end
def assign_values(weather_hash)
hourly_forecast_response = weather_hash.parsed_response['response']['forecast']['txt_forecast']['forecastdays']['forecastday'].first
self.fcttext = hourly_forecast_response['fcttext']
self.icon = hourly_forecast_response['icon_url']
end
def initialize(city)
#city = city
weather_hash = fetch_weather(city)
assign_values(weather_hash)
end
end
city_controller:
#weather_lookup = WeatherLookup.new(#city)
city_view:
= #weather_lookup.fcttext
= image_tag #weather_lookup.icon
This work fine...i get the fist dataset of the forecastdays container. The xml from the api looks like this:
<response>
<version>0.1</version>
<termsofService>
http://www.wunderground.com/weather/api/d/terms.html
</termsofService>
<features>
<feature>forecast</feature>
</features>
<forecast>
<txt_forecast>
<date>2:00 AM CEST</date>
<forecastdays>
<forecastday>
<period>0</period>
<icon>clear</icon>
<icon_url>http://icons-ak.wxug.com/i/c/k/clear.gif</icon_url>
<title>zondag</title>
<fcttext>
<![CDATA[ Helder. Hoog: 86F. Light Wind. ]]>
</fcttext>
<fcttext_metric>
<![CDATA[ Helder. Hoog: 30C. Light Wind. ]]>
</fcttext_metric>
<pop>0</pop>
</forecastday>
<forecastday>
<period>1</period>
<icon>clear</icon>
<icon_url>http://icons-ak.wxug.com/i/c/k/clear.gif</icon_url>
<title>zondagnacht</title>
<fcttext>
<![CDATA[ Helder. Laag: 61F. Light Wind. ]]>
</fcttext>
<fcttext_metric>
<![CDATA[ Helder. Laag: 16C. Light Wind. ]]>
</fcttext_metric>
<pop>0</pop>
</forecastday>
<forecastday>
<period>2</period>
<icon>partlycloudy</icon>
<icon_url>http://icons-ak.wxug.com/i/c/k/partlycloudy.gif</icon_url>
<title>maandag</title>
<fcttext>
<![CDATA[ Gedeeltelijk bewolkt. Hoog: 84F. Light Wind. ]]>
</fcttext>
<fcttext_metric>
<![CDATA[ Gedeeltelijk bewolkt. Hoog: 29C. Light Wind. ]]>
</fcttext_metric>
<pop>20</pop>
</forecastday>
<forecastday>
<period>3</period>
<icon>clear</icon>
<icon_url>http://icons-ak.wxug.com/i/c/k/clear.gif</icon_url>
<title>maandagnacht</title>
<fcttext>
<![CDATA[ Gedeeltelijk bewolkt. Laag: 63F. Light Wind. ]]>
</fcttext>
<fcttext_metric>
<![CDATA[ Gedeeltelijk bewolkt. Laag: 17C. Light Wind. ]]>
</fcttext_metric>
<pop>0</pop>
</forecastday>
I want to acces all the foracasts in the forecasts container by a loop, but when i change the hourly_forecast variable (.first) to .all or none i get the error message "can't convert String into Integer"
Someone ideas to fix this?
\
Once you remove .first from hourly_forecast, you are now returning a collection as opposed to a single item. That means your view won't work correctly so you'll need to update your view to render a collection. This is best done with a partial.
city_view will become:
= render partial: "weather_lookup", collection: #weather_lookup
Then create a partial named _weather_lookup.html.haml and include the following:
= #weather_lookup.fcttext
= image_tag #weather_lookup.icon
The underscore in front of the partial's filename is important as this is Rails convention so don't leave it out. When viewed, the partial will be rendered multiple times for every item in the collection.
Related
Trying to figure out how display the text and images I have scraped in my application/html.
Here is my app/scrape2.rb file
require 'nokogiri'
require 'open-uri'
url = "https://marketplace.asos.com/boutiques/independent-label"
doc = Nokogiri::HTML(open(url))
label = doc.css('#boutiqueList')
#label = label.css('#boutiqueList img').map { |l| p l.attr('src') }
#title = label.css("#boutiqueList .notranslate").map { |o| p o.text }
Here is the controller:
class PagesController < ApplicationController
def about
#used to change the routing to /about
end
def index
#label = label.css('#boutiqueList img').map { |l| p l.attr('src') }
#title = label.css("#boutiqueList .notranslate").map { |o| p o.text }
end
end
and finally the label.html.erb page:
<% #label.each do |image| %>
<%= image_tag image %>
<% end %>
do I need some other method, not storing the arrays properly?
Your controller needs to load the data itself, or somehow pull the data from scrape2.rb. Controllers do not have access to other files unless specified (include, extend, etc).
require 'nokogiri'
require 'open-uri'
class PagesController < ApplicationController
def index
# Call these in your controller:
url = "https://marketplace.asos.com/boutiques/independent-label"
doc = Nokogiri::HTML(open(url))
label = doc.css('#boutiqueList')
#label = label.css('#boutiqueList img').map { |l| p l.attr('src') }
#title = label.css("#boutiqueList .notranslate").map { |o| p o.text }
end
end
You're not parsing the data correctly.
label = doc.css('#boutiqueList')
should be:
label = doc.at('#boutiqueList')
#boutiqueList is an ID, of which only one can exist in a document at a time. css returns a NodeSet, which is like an Array, but you really want to point to the Node itself, which is what at would do. at is equivalent to search('...').first.
Then you use:
label.css('#boutiqueList img')
which is also wrong. label is supposed to already point to the node containing #boutiqueList, but then you want Nokogiri to look inside that node and find additional nodes with id="boutiqueList" and that contain <img> tags. But, again, because #boutiqueList is an ID and it can't occur more than once in the document, Nokogiri can't find any nodes:
label.css('#boutiqueList img').size # => 0
whereas using label.css correctly finds <img> nodes:
label.css('img').size # => 48
Then you use map to print out values, but map is used to modify the contents of an Array as it iterates over it. p will return the value it outputs, but it's bad form to rely on the returned value of p in a map. Instead you should map to convert the values, then puts the result if you need to see it:
#label = label.css('#boutiqueList img').map { |l| l.attr('src') }
puts #label
Instead of using attr('src'), I'd write the first line as:
#label = label.css('img').map { |l| l['src'] }
The same is true of:
#title = label.css("#boutiqueList .notranslate").map { |o| p o.text }
I'm running an else loop to iterate through an XML file in ruby, assigning values to a hash. There are 3 items in the XML file, but for some reason it only iterates through the first one, any idea why?
require "nokogiri"
f= File.open("untitled.xml")
doc = Nokogiri::XML(f)
f.close
doc.xpath('//item').each do |node|
children = node.children
item = {
"name" => node['name'],
"buyItNowPrice"=> children.css('buytItNowPrice').inner_text,
"description" => children.css('description').inner_text,
"startingBidPrice" => children.css('startingBidPrice').inner_text,
"closing_time" => children.css('closing_time').inner_text,
"closing_date" => children.css('closing_date').inner_text
}
puts item
end
XML:
<item name = "Test Thing">
<description>Something Coolest.</description>
<buytItNowPrice>154.99</buytItNowPrice>
<startingBidPrice>9999.99</startingBidPrice>
<closing_date>2014-12-25</closing_date>
<closing_time>12:32:PM</closing_time>
</item>
<item name = "Lazer">
<description>Something Cool.</description>
<buytItNowPrice>149.99</buytItNowPrice>
<startingBidPrice>9.99</startingBidPrice>
<closing_date>2014-12-25</closing_date>
<closing_time>12:32:PM</closing_time>
</item>
<item name = "Pokemon">
<description>Something even cooler.</description>
<buytItNowPrice>33.99</buytItNowPrice>
<startingBidPrice>9.99</startingBidPrice>
<closing_date>2014-12-25</closing_date>
<closing_time>12:32:PM</closing_time>
</item>
Output is only the the first item printed.
The given sample XML isn't valid.
A valid XML document requires a single root node, right now you have 3.
You could fix this by wrapping all the <item> nodes in a <items> root node, and iterate through its children then.
I've created a tmpl gsp tag containing a bit of markup that's used throughout the forms in my webapp (/shared/formRow.gsp). I'd like to reference this tmpl gsp tag in a groovy taglib I've created. Is this possible?
Here's the def from my taglib...
def checkboxRow = { attrs ->
def name = attrs.name
def value = attrs.value
def label = attrs.label
def defaultLabel = attrs.defaultLabel
out << "<tmpl:/shared/formRow name='${name}' label='${label}' defaultLabel='${defaultLabel}'>"
out << " ${checkBox(id: name, name: name, value: value)}"
out << "</tmpl:/shared/formRow>"
}
I realise the syntax is a bit different in taglibs (e.g. you need to do ${checkBox(...)} rather than ), but is it possible to reference your own tmpl gsp tag in a similar way? If so, what syntax would I use?
Well, it turns out that it's in the Grails documentation, here.
You should just call the render template method like this:
def formatBook = { attrs, body ->
out << render(template: "bookTemplate", model: [book: attrs.book])
}
Simple really!
I am trying to use a groovy function inside a GSP. Please help as I am about to tare my hair out here.
At the top of my GSP i have <%# page import = company.ConstantsFile %>
Inside my GSP I have
<p>
I have been in the heating and cooling business for <%(ConstantsFile.daysBetween())%>
</p>
and my ConstantsFile.groovy
package company
import static java.util.Calendar.*
class ConstantsFile {
def daysBetween() {
def startDate = Calendar.instance
def m = [:]
m[YEAR] = 2004
m[MONTH] = "JUNE"
m[DATE] = 26
startDate.set(m)
def today = Calendar.instance
render today - startDate
}
}
I have also tried changing renter to puts, system.out, etc but that isn't my main problem.
Error 500: Internal Server Error
URI
/company/
Class
java.lang.NullPointerException
Message
Cannot invoke method daysBetween() on null object
So I try
<p>
I have been in the heating and cooling business for <%(new ConstantsFile.daysBetween())%>
</p>
but then i get
Class: org.codehaus.groovy.control.MultipleCompilationErrorsException
unable to resolve class ConstantsFile.daysBetween # line 37, column 1. (new ConstantsFile.daysBetween()) ^ 1 error
Please someone help me or point me to a website that shows what to do.. I have tried googling and everything talks about a g:select or some other kind of tag... I just want to output the result of the function like I used to in the JSPs.
First, your GSP's import should be:
<%# page import="company.ConstantsFile" %>
Second, your daysBetween should be static (it makes more sense) and you don't render from anything but a controller:
class ConstantsFile {
static daysBetween() {
def startDate = Calendar.instance
def m = [:]
m[YEAR] = 2004
m[MONTH] = "JUNE"
m[DATE] = 26
startDate.set(m)
def today = Calendar.instance
return today - startDate
}
}
Third, access it in the following way:
<p>I have been in the heating and cooling business for ${ConstantsFile.daysBetween}</p>
And lastly, you should use a taglib for this. I'm editing my post now to add an example
class MyTagLib {
static namespace = "my"
def daysBetween = { attr ->
out << ConstantsFile.daysBetween()
}
}
Then use in your GSP
<p>I have been in the heating and cooling business for <my:daysBetween /></p>
I have a collection of Blog items.
#blogs = Blog.find(:all)
Each blog has a description textfield with some text. What I would like to do is splitting the #blogs objects into 3 divs, but with roughly the same characters in each column.
<div id="left">
#blog1 (653 characters)
</div>
<div id="center">
#blog2 (200 characters)
#blog5 (451 characters)
</div>
<div id="right">
#blog3 (157 characters)
#blog4 (358 characters)
#blog6 (155 characters)
</div>
I can't figure out how to do that without getting really complicated and probably inefficient.
So far I have thought about converting the description field (size) to % of total characters in the #blogs collection, but how do I match/split the elements, so that I get closest to 33% in each column - like a super simple tetris game :)
Any thoughts?
Here's a quick hack that isn't perfect, but might get you pretty close. The algorithm is simple:
Sort items by size.
Partition items into N bins.
Resort each bin by date (or other field, per your desired presentation order)
Here's a quick proof of concept:
#!/usr/bin/env ruby
# mock out some simple Blog class for this example
class Blog
attr_accessor :size, :date
def initialize
#size = rand(700) + 100
#date = Time.now + rand(1000)
end
end
# create some mocked data for this example
#blogs = Array.new(10) { Blog.new }
# sort by size
sorted = #blogs.sort_by { |b| b.size }
# bin into NumBins
NumBins = 3
bins = Array.new(NumBins) { Array.new }
#blogs.each_slice(NumBins) do |b|
b.each_with_index { |x,i| bins[i] << x }
end
# sort each bin by date
bins.each do |bloglist|
bloglist.sort_by! { |b| b.date }
end
# output
bins.each_with_index do |bloglist,column|
puts
puts "Column Number: #{column+1}"
bloglist.each do |b|
puts "Blog: Size = #{b.size}, Date = #{b.date}"
end
total = bloglist.inject(0) { |sum,b| sum + b.size }
puts "TOTAL SIZE: #{total}"
end
For more ideas, look up the multiprocessor scheduling problem.