Weird problem. If the class at the bottom was a module, split the Json without problems, if it was only methods, also works, but the problem is.. when it is a class, it does not split the Json anymore, and returns an empty array.. however, if being a class, I do a puts the object, it actually puts it..
Any thoughts about why? How can I fix it?
I have this controller:
def index
begin
call_employee_work_locations_api
rescue => ex
render :json => {"service unavailable": "0001" }, :status => :service_unavailable
end
end
I have this service:
def call_employee_work_locations_api
auth = {:username=>ENV["USERNAME"], :password=>ENV["PASSWORD"]}
employee_locations = HTTParty.get(employee_work_Location_url , :basic_auth => auth)
#serialize_work_location(employee_locations)
serializer = EmployeeSerializer.new
serializer.serialize_work_location(employee_locations)
end
I have this builder:
json.array!(#top_locations) do |location|
json.extract! location, :name, :description, :latitude, :longitude
end
I have this class:
class EmployeeSerializer
def serialize_work_location(employee_locations)
employee_locations= JSON.parse(employee_locations)
locations=[]
employee_locations["work_locations"].each do |attributes|
location = Location.new(attributes["latitude"],attributes["longitude"],attributes["description"],attributes["name"])
locations.push(location)
end
employee_locations_selector(locations)
end
def top_office_location_selector(locations, city)
top_locations=[]
locations.each do |office|
if office.name == city[0] then top_locations.push(office) end
if office.name == city[1] then top_locations.push(office) end
end
#top_locations = top_locations
p #top_locations <--- it prints the object perfectly, but does not pass to the view, I get an empty array instead.
end
def employee_locations_selector(locations)
city = locations.each_with_object(Hash.new(0)) { |locations, counts| counts[locations.name] += 1 }.max_by{|k,v| v}
top_office_location_selector(locations, city)
end
end
The instance variable #top_locations is being set within the scope of the EmployeeSerializer class, not your controller. As such it's just a normal instance variable and so Rails knows nothing about it. You can assign the return value of #top_office_location_selector to an instance variable in the controller and it should work.
On a side note, the code would be cleaned up a lot by using #map over #each.
Related
I have this import method in my active record which I use to import the csv file. I want to know how to do the error handling of this in the active record.
class SheetEntry < ActiveRecord::Base
unloadable
belongs_to :user
belongs_to :project
belongs_to :task
validate :project_and_task_should_be_active
def self.import(csv_file)
attributes = [:user_id, :project_id, :task_id, :date, :time_spent, :comment]
errors=[]
output = {}
i=0
CSV.foreach(csv_file, headers: true, converters: :date).with_index do |row,j|
entry_hash= row.to_hash
entry_hash['Project'] = SheetProject.where("name= ?" , entry_hash['Project']).pluck(:id)
entry_hash['Task'] = SheetTask.where("name= ?" , entry_hash['Task']).pluck(:id)
entry_hash['Date'] = Time.strptime(entry_hash['Date'], '%m/%d/%Y').strftime('%Y-%m-%d')
entry_hash['Time (Hours)'] = entry_hash['Time (Hours)'].to_f
firstname = entry_hash['User'].split(" ")[0]
lastname = entry_hash['User'].split(" ")[1]
entry_hash['User'] = User.where("firstname=? AND lastname=?",firstname,lastname).pluck(:id)
entry_hash.each do |key,value|
if value.class == Array
output[attributes[i]] = value.first.to_i
else
output[attributes[i]] = value
end
i += 1
end
entry=SheetEntry.new(output)
entry.editing_user = User.current
entry.save!
end
end
def project_and_task_should_be_active
errors.add(:sheet_project, "should be active") unless sheet_project.active?
errors.add(:sheet_task, "should be active") if sheet_task && !sheet_task.active?
end
end
I want to know how to show the error if there is a nil object returned for either entry_hash['Project'] or entry_hash['Task'] or for any of the fields in the csv.
For example: If the user had entered the wrong project or wrong task or wrong date. I want the error to be shown along with the line no and stop the uploading of the csv. Can someone help?
You can use begin and rescue statements to handle errors in any ruby classes.
You can use the rescue block to return the Exception e back to the caller.
However, you cannot call errors.add method to add error because #errors is an instance method which is not accessible inside class method self.import.
def self.import(csv_file)
begin
attributes = [:user_id, :project_id, :task_id, :date, :time_spent, :comment]
errors=[]
output = {}
i=0
CSV.foreach(csv_file, headers: true, converters: :date).with_index do |row,j|
...
end
rescue Exception => e
return "Error: #{e}"
end
end
I have a few class methods that help with querying the database but I'd like to add some sort of validations for the params sent to these methods. For example,
def self.get_ayahs_by_range(surah_id, from, to)
self.where('quran.ayah.surah_id = ?', surah_id)
.where('quran.ayah.ayah_num >= ?', from)
.where('quran.ayah.ayah_num <= ?', to)
.order('quran.ayah.surah_id, quran.ayah.ayah_num')
end
which is called from the controller by passing params[:surah_id], params[:to] and params[:from] to this function.
At times, for some reason, we have :surah_id being undefined which causes a mess. How can I fix by validations prior to?
Any suggestions for params validation other than strong_params which didn't work for an index action I felt?
controller:
def index
unless valid_params?
return render json: {message: 'Params are wrong.'}
end
params_hash = (params[:range] || ("#{params[:from]}-#{params[:to]}")) + "/#{params[:quran]}/#{params[:audio]}/#{params[:content]}"
if params.key?(:range)
range = params[:range].split('-')
elsif params.key?(:from) && params.key?(:to)
range = [params[:from], params[:to]]
else
range = ['1', '10']
end
if (range.last.to_i - range.first.to_i) > 50
return render json: {error: "Range invalid, use a string (maximum 50 ayat per request), e.g. '1-3'"}
end
#results = Rails.cache.fetch("surahs/#{params[:surah_id]}/ayahs/#{params_hash}", expires_in: 12.hours) do
ayahs = Quran::Ayah.get_ayahs_by_range(params[:surah_id], range[0], range[1])
Quran::Ayah.merge_resource_with_ayahs(params, ayahs)
end
render json: #results
end
Make a new simple class in your models (it doesn't have to be a table in your database)
require 'ostruct'
class SearchOptions < OpenStruct
include ActiveModel::Validations
validates :surah_id, presence: true
validates :from, presence: true
...
end
Then in the controller
#search_option = SearchOption.new(seach_params)
#search_option.valid?
# here you put the "invalid" processing
# maybe re-render the search parameters view
end
I'm learning Ruby on Rails and got curious how the params method works. I understand what it does, but how?
Is there a built-in method that takes a hash string like so
"cat[name]"
and translates it to
{ :cat => { :name => <assigned_value> } }
?
I have attempted to write the params method myself but am not sure how to write this functionality in ruby.
The GET parameters are set from ActionDispatch::Request#GET, which extends Rack::Request#GET, which uses Rack::QueryParser#parse_nested_query.
The POST parameters are set from ActionDispatch::Request#POST, which extends Rack::Request#POST, which uses Rack::Multipart#parse_multipart. That splays through several more files in lib/rack/multipart.
Here is a reproduction of the functionality of the method (note: this is NOT how the method works). Helper methods of interest: #array_to_hash and #handle_nested_hash_array
require 'uri'
class Params
def initialize(req, route_params = {})
#params = {}
route_params.keys.each do |key|
handle_nested_hash_array([{key => route_params[key]}])
end
parse_www_encoded_form(req.query_string) if req.query_string
parse_www_encoded_form(req.body) if req.body
end
def [](key)
#params[key.to_sym] || #params[key.to_s]
end
def to_s
#params.to_s
end
class AttributeNotFoundError < ArgumentError; end;
private
def parse_www_encoded_form(www_encoded_form)
params_array = URI::decode_www_form(www_encoded_form).map do |k, v|
[parse_key(k), v]
end
params_array.map! do |sub_array|
array_to_hash(sub_array.flatten)
end
handle_nested_hash_array(params_array)
end
def handle_nested_hash_array(params_array)
params_array.each do |working_hash|
params = #params
while true
if params.keys.include?(working_hash.keys[0])
params = params[working_hash.keys[0]]
working_hash = working_hash[working_hash.keys[0]]
else
break
end
break if !working_hash.values[0].is_a?(Hash)
break if !params.values[0].is_a?(Hash)
end
params.merge!(working_hash)
end
end
def array_to_hash(params_array)
return params_array.join if params_array.length == 1
hash = {}
hash[params_array[0]] = array_to_hash(params_array.drop(1))
hash
end
def parse_key(key)
key.split(/\]\[|\[|\]/)
end
end
I am trying to make a search with custom data in this action here below,
def number_price(user_id,to)
user = User.find(user_id)
prices = Price.where(user_id: user_id)
price.each do |price|
if to =~ /^(price.prefix)/
return price."price_#{user_currency.downcase}"
else
return DefaultPrices."price_#{user_currency.downcase}"
end
end
But i having an error with this request here on the line;
return price."price_#{user_currency.downcase}"
Any idea how i can improve this and make it work.. i have a feeling it something silly..
Thank you
I'm not sure how your model looks like
But I guess you are trying to achieve dynamic function call which can be done by
return price.send("price_#{user_currency.downcase}")
Or
return eval "price.price_#{user_currency.downcase}"
Use public_send instead send because public_send invoke only public method on class instance:
class Foo
private
def bar
puts "Hi!"
end
end
=> nil
=> f = Foo.new
=> #<Foo:0x007f83e3813af8>
=> f.bar
=> #NoMethodError: private method `bar' called for #<Foo:0x007f83e3813af8>
=> f.send(:bar)
Hi!
=> nil
class Baz
def bor
puts "Ho!"
end
end
=> nil
=> s = Baz.new
=> #<Baz:0x007f83e2429da8>
=> s.bor
Ho!
=> nil
=> s.public_send(:bor)
Ho!
=> nil
I'm currently using an API and not a database, and I want to be as close as ActiveRecord, so I decided to go ahead and do exactly like this railscast here: http://railscasts.com/episodes/219-active-model
So far, my save method works well, so I can save data to the API. My problem is with the edit, my find method seems to be the problem... Here is some code!
Edit method in my controller
def edit
#parking = Parking.find(params[:id])
end
Whole model
class Parking
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :name, :address, :city, :longitude, :latitude, :contributor_name, :contributor_email
validates_presence_of :name, :address, :city, :longitude, :latitude
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def self.find(id)
parking = Parse.get("Parking", id.to_s) // API Call
name = parking["name"]
address = parking["address"]
city = parking["city"]
longitude = parking["location"]["longitude"]
latitude = parking["location"]["latitude"]
contributor_name = parking["contributorName"]
contributor_email = parking["contributorEmail"]
return self
end
def save
if (!valid?)
return false
else
parking = Parse::Object.new("Parking")
data =
{
:longitude => 40.0,
:latitude => -30.0
}
point = Parse::GeoPoint.new(data)
parking["location"] = point
parking["name"] = name
parking["address"] = address
parking["city"] = city
parking["contributorName"] = contributor_name
parking["contributorEmail"] = contributor_email
if (parking.save)
return true
end
end
end
def persisted?
false
end
end
Here is the error I currently get:
undefined method `to_key' for Parking:Class
Extracted source (around line #1):
1: <%= form_for(#parking, :html => { :class => "form-horizontal"}) do |f| %>
2: <% if #parking.errors.any? %>
3: <div class="alert alert-error fade in">
4: <a class="close" data-dismiss="alert">×</a>**
If anybody as suggestions, I'm open to any ideas really, I'm beginning with rails :)
Thanks!
EDIT:
When I do in my controller edit method something like:
def edit
#parking = Parking.new
#parking.name = "foo"
#parking.address = "foo"
#parking.city = "foo"
#parking.longitude = "foo"
#parking.latitude = "foo"
end
My view load foo in every fields no problemo, so the problem is I must be doing something wrong with the find method :)
One problem is that your find method is a class method (by virtue of being 'self.find'), meaning it does not operate on an instance of the class and therefore has no knowledge of instance variables/methods such as name, address etc.
A better way to implement find is to instantiate a new instance of Parking, and populate it's variables, then return it e.g.
def self.find(id)
raw = Parse.get("Parking", id.to_s)
parking = Parking.new
parking.name = raw["name"]
# etc for the others
parking # Return the newly-created instance
end
This doesn't explain the 'undefined method' you're currently seeing, you may need to post up more detail to get an answer for that, particularly a full backtrace from the exception to see which bit of code is actually raising it. From the information you've supplied I'd guess that something within the Parse.get method is causing it.