How to parse variable in wunderground api url with HTTPParty? - ruby-on-rails

Use wunderground API to show weather forecast on my city pages.
city_controller.rb
def show
#region = Region.find(params[:region_id])
#city = City.find(params[:id])
#weather_lookup = WeatherLookup.new
end
weather_lookup.rb
class WeatherLookup
attr_accessor :temperature, :icon, :condition
def fetch_weather
HTTParty.get("http://api.wunderground.com/api/a8135a01b8230bfb/hourly10day/lang:NL/q/IT/#{#city.name}.xml")
end
def initialize
weather_hash = fetch_weather
end
def assign_values(weather_hash)
hourly_forecast_response = weather_hash.parsed_response['response']['hourly_forecast']['forecast'].first
self.temperature = hourly_forecast_response['temp']['metric']
self.condition = hourly_forecast_response['condition']
self.icon = hourly_forecast_response['icon_url']
end
def initialize
weather_hash = fetch_weather
assign_values(weather_hash)
end
end
show.html.haml(city)
= #weather_lookup.temperature
= #weather_lookup.condition.downcase
= image_tag #weather_lookup.icon
To fetch to correct weather forecast i thought that i can place the #city variable in the HTTParty.get URL as i did in the example, But i get the error message undefined method `name'
What am I doing wrong here?

If you need the city in WeatherLookup you are going to need to pass it to the initializer. Instance variables are only bound to their respective views.
#weather_lookup = WeatherLookup.new(#city)
attr_accessor :city # optional
def initialize(city)
#city = city
weather_hash = fetch_weather
end

Related

getting NoMethodError Undefined method in Ruby

getting NoMethodError Undefined method service_account_id under valid_restriction? method.
Can anybody check why am I getting this error?
If you have links to resolve this, that would be helpful too. Thanks.
Error:
ERROR:
<NoMethodError: undefined method `service_account_id' for #String:0x0000560784713130>
authentication_request.rb:19:in `valid_restriction?'
Code Snippet below:
module Authentication
module AuthnGcp
class DecodedToken
PROJECT_ID_TOKEN_CLAIM_NAME = "google/compute_engine/project_id"
INSTANCE_NAME_TOKEN_CLAIM_NAME = "google/compute_engine/instance_name"
SUB_TOKEN_CLAIM_NAME = "sub"
EMAIL_TOKEN_CLAIM_NAME = "email"
AUDIENCE_TOKEN_CLAIM_NAME = "aud"
attr_reader :project_id, :instance_name, :service_account_id, :service_account_email, :audience
def initialize(decoded_token_hash:, logger:)
#decoded_token_hash = decoded_token_hash
#logger = logger
initialize_required_claims
initialize_optional_claims
end
private
def initialize_required_claims
#audience = required_token_claim_value(AUDIENCE_TOKEN_CLAIM_NAME)
#service_account_id = required_token_claim_value(SUB_TOKEN_CLAIM_NAME)
end
def initialize_optional_claims
#service_account_email = optional_token_claim_value(EMAIL_TOKEN_CLAIM_NAME)
#project_id = optional_token_claim_value(PROJECT_ID_TOKEN_CLAIM_NAME)
#instance_name = optional_token_claim_value(INSTANCE_NAME_TOKEN_CLAIM_NAME)
end
def required_token_claim_value(required_token_claim)
required_token_claim_value = token_claim_value(required_token_claim)
if required_token_claim_value.nil? || required_token_claim_value.empty?
raise Errors::Authentication::Jwt::TokenClaimNotFoundOrEmpty, required_token_claim
end
log_claim_extracted_from_token(required_token_claim, required_token_claim_value)
required_token_claim_value
end
def optional_token_claim_value(optional_token_claim)
optional_token_claim_value = token_claim_value(optional_token_claim)
if optional_token_claim_value.nil? || optional_token_claim_value.empty?
optional_token_claim_value = nil
#logger.debug(LogMessages::Authentication::Jwt::OptionalTokenClaimNotFoundOrEmpty.new(optional_token_claim))
else
log_claim_extracted_from_token(optional_token_claim, optional_token_claim_value)
end
optional_token_claim_value
end
def token_claim_value(token_claim)
token_claim_path = token_claim.split('/')
#decoded_token_hash.dig(*token_claim_path)
end
def log_claim_extracted_from_token(token_claim, token_claim_value)
#logger.debug(
LogMessages::Authentication::Jwt::ExtractedClaimFromToken.new(
token_claim,
token_claim_value
)
)
end
end
end
end
==========================================================================
module Authentication
module AuthnGcp
# This class is responsible for retrieving the correct value from the GCP token
# of the requested attribute.
class AuthenticationRequest
def initialize(decoded_token:)
#decoded_token = decoded_token
end
def valid_restriction?(restriction)
token_value =
case restriction.name
when Restrictions::PROJECT_ID
#decoded_token.project_id
when Restrictions::INSTANCE_NAME
#decoded_token.instance_name
when Restrictions::SERVICE_ACCOUNT_ID
#decoded_token.service_account_id
when Restrictions::SERVICE_ACCOUNT_EMAIL
#decoded_token.service_account_email
end
raise Errors::Authentication::AuthnGcp::JwtTokenClaimIsMissing, restriction.name if token_value.blank?
token_value == restriction.value
end
end
end
end

NoMethodError undefined method `[]=' for nil:NilClass

I created multiple classes with one test method to test wither the ruby objects get serialized correctly.
The error returned:
undefined method `[]=' for nil:NilClass
from /Users/horse/workspace/queryapi/app/models/query_model.rb:193:in `serialize'
I run the below test_query method through the rails console by initializing QueryModelTester and then invoking test_query() method on that object.
My code:
class QueryModelTester
def test_query
must = Must.new
range_criteria = RangeCriteria.new
range_criteria.gte = 20140712
range_criteria.lte = 1405134711
range = RangeBuilder.new
range.search_field = "created_time"
range.range_criteria = range_criteria
must.range = range
bool = Bool.new
bool.must = must
main_query = bool.serialize
puts main_query
end
end
Here are the model classes the above class is testing:
class RangeCriteria
#query_hash = Hash.new
attr_accessor :gte, :lte
def serialize
if(#gte.present?)
#query_hash[:gte] = #gte
end
if(#lte.present?)
#query_hash[:lte] = #lte
end
if(#gte.present? || #lte.present?)
return #query_hash
end
end
end
class RangeBuilder
#query_hash = Hash.new
attr_accessor :search_field, :range_criteria
def serialize
if(#search_field.present?)
#query_hash[#search_field] = #range_criteria.serialize
return #query_hash[:range] = #query_hash
end
end
end
class Bool
#query_hash = {}
attr_accessor :must
def serialize
if( #must.present? )
#query_hash[:must] = #must.serialize
return #query_hash[:bool] = #query_hash
end
end
end
The problem is when you initialize your #query_hash. In all your classes they are initialized in wrong scope. To fix it, you should move #query_hash = Hash.new to initialize method, like:
class RangeCriteria
def initialize
#query_hash = Hash.new
end
# ...
end
class RangeBuilder
def initialize
#query_hash = Hash.new
end
# ...
end
class Bool
def initialize
#query_hash = Hash.new
end
# ...
end
Hope that helps.
Good luck!

ruby on rails accessing custom class attributes from its object

I have a custom class in my application controller. Like below:
class Defaults
def initialize
#value_1 = "1234"
#value_2 = nil
#data = Data.new
end
end
class Data
def initialize
#data_1 = nil
end
end
Now in my controller method i have created an object of type Defaults
def updateDefaultValues
defaults = Defaults.new
# i am unable to update the value, it says undefined method
defaults.value_2 = Table.maximum("price")
defaults.data.data_1 = defaults.value_2 * 0.3
end
How to access value_2 from defaults object?
defaults.value_2
Also, how to access data_1 attribute from data object within defaults object?
defaults.data.data_1
You should use attr_accessor:
class Defaults
attr_accessor :value_1, :value_2, :data
# ...
end
defaults = Defaults.new
defaults.value_1 = 1
# => 1
defaults.value_1
# => 1
As you are using def as a keyword to define the method, that means def is a reserved keyword. You can't use reserved keywords as a variable.
You just need to rename your variable name from def to something_else and it should work! Your code will look like this:
def updateDefaultValues
obj = Defaults.new
obj.value_2 = Table.maximum("price")
obj.data.data_1
end
EDIT:
As per OP's comment & updated question, he had used def just as an example, here is the updated answer:
You may need attr_accessor to make attrs accessible:
class Defaults
attr_accessor :value_1, :value_2, :data
...
...
end
class Data
attr_accessor :data_1
...
...
end
Add value_2 method in Defaults class
class Defaults
def initialize
#value_1 = "1234"
#value_2 = nil
#data = Data.new
end
def value_2
#value_2
end
end
class Data
def initialize
#data_1 = nil
end
end

Nokogiri Scraping In Rails

So I have this code in my index action, would love to move it to a model, just a little confused on how to do it.
Original Code
def index
urls = %w[http://cltampa.com/blogs/potlikker http://cltampa.com/blogs/artbreaker http://cltampa.com/blogs/politicalanimals http://cltampa.com/blogs/earbuds http://cltampa.com/blogs/dailyloaf http://cltampa.com/blogs/bedpost]
#final_images = []
#final_urls = []
urls.each do |url|
blog = Nokogiri::HTML(open(url))
images = blog.xpath('//*[#class="postBody"]/div[1]//img/#src')
images.each do |image|
#final_images << image
end
story_path = blog.xpath('//*[#class="postTitle"]/a/#href')
story_path.each do |path|
#final_urls << path
end
end
end
I tested this code in my model and it works perfectly for one url, just not sure how to integrate all of the urls like the original code.
New Code
Model
class Photocloud < ActiveRecord::Base
attr_reader :url, :data
def initialize(url)
#url = url
end
def data
#data ||= Nokogiri::HTML(open(url))
end
def get_elements(path)
data.xpath(path)
end
end
Controller
def index
#scraper = Photocloud.new('http://cltampa.com/blogs/artbreaker')
#photos = #scraper.get_elements('//*[#class="postBody"]/div[1]//img/#src')
#story_urls = #scraper.get_elements('//*[#class="postBody"]/div[1]//img/#src')
end
My main questions are how would I initialize multiple urls and loop through them like my original code. I have tried different things but feel like I have hit a wall. I need to save them to the database, but would like to get this working first. Any help is greatly appreciated.
Updated Controller - WIP
def index
start_urls = %w[http://cltampa.com/blogs/potlikker
http://cltampa.com/blogs/artbreaker
http://cltampa.com/blogs/politicalanimals
http://cltampa.com/blogs/earbuds
http://cltampa.com/blogs/dailyloaf
http://cltampa.com/blogs/bedpost]
#scraper = Photocloud.new(start_urls)
#images =
#paths =
end
Need some help with this part...
It seems that you don't persist scraped images and paths to the database so Photocloud doesn't need to inherit from ActiveRecord::Base - it can be just a plain old ruby object (PORO):
class Photocloud
attr_reader :start_urls
attr_accessor :images, :paths
def initialize(start_urls)
#start_urls = start_urls
#images = []
#paths = []
end
def scrape
start_urls.each do |start_url|
blog = Nokogiri::HTML(open(url))
scrape_images(blog)
scrape_paths(blog)
end
end
private
def scrape_images(blog)
images = blog.xpath('//*[#class="postBody"]/div[1]//img/#src')
images.each do |image|
images << image
end
end
def scrape_paths(blog)
story_path = blog.xpath('//*[#class="postTitle"]/a/#href')
story_path.each do |path|
paths << path
end
end
end
In controller:
scraper = Photocloud.new(start_urls)
scraper.scrape
#images = scraper.images
#paths = scraper.paths
This is only one of the possibilities how you could structure code, of course.

Make GeoKit play nicely with my Address class

My Address class has a geocode class method that returns an array of address objects derived from geocoding the method's parameter (if the geocoding results in an exact match, the array will have one element).
One annoying part about writing this method is translating the GeoKit address objects to my address objects (e.g., "street_address" -> "address1"). Is there a better way to do this?
class Address < ActiveRecord::Base
def self.geocode(string)
return nil if string.nil?
results = Geokit::Geocoders::GoogleGeocoder.geocode(string)
address_objects = Array.new
results.all.each do |r|
params = Hash.new
params['address1'] = r.street_address
params['city'] = r.city
params['zipcode'] = r.zip
params['state'] = State.find_by_abbr(r.state)
params['country'] = Country.find_by_iso(r.country_code)
new_address = Address.new(params)
new_address.single_line_address = r.full_address
address_objects << new_address
end
return address_objects
end
end
What about the following
class Address < ActiveRecord::Base
def self.geocode(string)
return if string.nil?
results = Geokit::Geocoders::GoogleGeocoder.geocode(string)
results.all.map do |r|
Address.new do |address|
address.address1 = r.street_address
address.city = r.city
address.zipcode = r.zip
address.state = State.find_by_abbr(r.state)
address.country = Country.find_by_iso(r.country_code)
address.single_line_address = r.full_address
end
end
end
end

Resources