I'm just beginning to (hopefully!) learn programming / ruby on rails and trying to push the results of a hash to an array using:
ApplicationController:
def css_class
css = Array.new
product = {#product.oil => ' oil', #product.pressure_meters => ' pressure_meters', #product.commercial => 'commercial'}
product.each do |key, value|
if key == true
css.push(value)
end
end
сss.join
end
And this in the ProductsController:
def create
#product = Product.new(params[:product])
#product.css_class = css_class
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
format.json { render json: #product, status: :created, location: #product }
else
format.html { render action: "new" }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
This only seems to only save the last thing that was pushed to the array, I tried the below code on it's own and it seems to work, so I'm baffled as to where I'm going wrong?
def css_class
css = Array.new
product = {1 => ' pressure_meters', 2 => ' oil'}
product.each do |key, value|
if key > 0
css.push(value)
end
end
css.join
end
puts css_class
Thanks in advance.
In Ruby Hash can't have duplicate keys so
def css_class
css = Array.new
product = { #product.oil => ' oil',
#product.pressure_meters => ' pressure_meters',
#product.commercial => 'commercial' }
product.each do |key, value|
if key == true
css.push(value)
end
end
сss.join
end
will not work because
irb(main):0> h = { true => 'foo', true => 'bar', false=>'foo', false => 'bar' }
=> {true=>"bar", false=>"bar"}
your second example works only because you have distinct keys (1,2) so let's refactor your code a bit
def css_class
css = ""
product = { ' oil' => #product.oil,
' pressure_meters' => #product.pressure_meters,
' commercial' => #product.commercial }
product.each do |key, value|
css << key if value
end
сss.strip
end
it can be simplified even more however previous version should work fine too
def css_class
[ "oil ", "pressure_meters ", "commercial " ].inject(""){ |sum, val| sum += val if #product.send( val.strip ) }.strip
end
You can use Hash#values to get an array of your hash's values.
So:
product_values = product.values
And conditionally, you could pick the ones you want using select, like this:
product_values = product.select {|k,v| k == true }.values
Which is verbose for:
product_values = product.select {|k,v| k }.values
Thanks for pointing me in the right direction. I kept getting a 500 internal server error with your code Bohdan, not sure why, but played around with it and eventually found this to work:
def css_class
css = Array.new
product = { ' oil' => #product.oil,
' pressure_meters' => #product.pressure_meters,
' commercial' => #product.commercial }
product.each do |key, value|
css << key if value
end
css.join
end
Related
Given that each project has_many :tasks, I hope to render the project.task within the json result.
However, the json output also include a list of individual tasks as part of the result. See below:
#tasks = Task.all.reject do |i|
i.project.inbox == false || i.completion_status == 100
end
#projects = Project.all.reverse.reject do |i|
i.inbox == true || i.completion_status == 100
end
#all = #tasks + #projects
respond_to do |format|
format.html
format.json { paginate json: #all.sort_by(&:created_at).reverse,
per_page: 25 }
end
This means that if I simply include:
respond_to do |format|
format.html
format.json { paginate json: #all.sort_by(&:created_at).reverse,
:include => [:tasks => {:only => :id}],
per_page: 25 }
end
Rails will throw an error of undefined method tasks for Task:0x007fa0ad8d3858 since tasks does not have a task method.
How can I have the project.tasks appear in a json result which also include individual tasks result? Thank you.
Consider using active_model_serializers gem. After installing you can define a serializer for Project model like so:
class ProjectSerializer < ActiveModel::Serializer
attributes :id, :created_at, :tasks
def tasks
object.tasks.map(&:id)
end
end
Note: There might be any attributes you need. It's just an example.
Then you can do:
#projects = Project.all.reverse.reject do |i|
i.inbox == true || i.completion_status == 100
end
serialized_projects = ActiveModelSerializers::SerializableResource.new(#projects, each_serializer: ProjectSerializer).as_json
It will return you an array:
[{:id => 1, :created_at => "2017-07-13 08:13:20", tasks => [1, 2, 3, ...]}, ...]
Then for json response you can concat #tasks and serialized_projects:
all_for_json = #tasks + serialized_projects
And finally you can sort it like this:
all_for_json.sort_by { |record| record[:created_at] }.reverse
Note that you should do exactly record[:created_at], because projects are hashes, not active record models.
But I don't think this is a good idea to mix hashes and active record models in one array. So there is another solution.
You can also define a serializer for Task model:
class TaskSerializer < ActiveModel::Serializer
attributes :id, :created_at
end
Note: There might be any attributes you need. It's just an example.
And override code like this:
#tasks = Task.all.reject do |i|
i.project.inbox == false || i.completion_status == 100
end
#projects = Project.all.reverse.reject do |i|
i.inbox == true || i.completion_status == 100
end
respond_to do |format|
format.html do
#all = #tasks + #projects
end
format.json do
serialized_tasks = ActiveModelSerializers::SerializableResource.new(#tasks, each_serializer: TaskSerializer).as_json
serialized_projects = ActiveModelSerializers::SerializableResource.new(#projects, each_serializer: ProjectSerializer).as_json
all_serialized = serialized_tasks + serialized_projects
paginate json: all_serialized.sort_by { |record| record[:created_at] }.reverse, per_page: 25
end
end
To DRY your code, you can put
ActiveModelSerializers::SerializableResource.new(...).as_json
to separate method. For example:
def serialize_collection(collection, each_serializer)
ActiveModelSerializers::SerializableResource.new(collection, each_serializer: each_serializer).as_json
end
And do serializations like this:
serialized_tasks = serialize_collection(#tasks, TaskSerializer)
serialized_projects = serialize_collection(#projects, ProjectSerializer)
Profits of this solution:
You don't mix active record models and hashes in one array.
You can easily define via serializers which attributes and associations to include and set custom names for them.
I am working on functionality whereby a table is displayed with records, each with radio buttons Reject and Approve. A user selects the appropriate radio button and presses process. Control is passed to process_campaigns. From here it breaks down the data and analyses each record's status. If it is approved it redirects to approve block and same with reject.
The following parameters are passed:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"9TCExvCmDahAWGQloPdaRkSowbBaxZGwZnRa8sbNkWM=", "campaign"=>{"2"=>{"start_date"=>"2016-07-18 15:43:00", "end_date"=>"2016-10-15 12:20:00", "merchant_revenue"=>"10", "status"=>"Approved", "notes"=>""}, "1"=>{"start_date"=>"2016-07-15 12:20:00", "end_date"=>"", "merchant_revenue"=>"10", "status"=>"Approved", "notes"=>""}}, "Commit"=>"Process"}
def process_campaign
authorize! :operator, current_user.operator
params[:campaign].each do |key, value|
if value[:status] == "Approved"
redirect_to approve_operator_campaign_path(key), :id => key, :start_date => value[:start_date], :revenue_mode => value[:revenue_model], :end_date => value[:end_date], :active => true, :status => 307 and return
elsif value[:status] == "Rejected"
redirect_to reject_operator_campaign_path(key), campaign_name: key, notes: value[:notes], :status => 307 and return
end
end
redirect_to operator_campaigns_path, flash: { notice: "Campaigns have been processed."}
end
def reject
authorize! :operator, current_user.operator
params[:campaign].each do |key, value|
if value[:status] = "Rejected"
#campaign = Campaign.active.where(id: key, operator_id: current_user.operator_id).last!
#campaign.data.merge!({:notes=>value[:notes]})
#campaign.status = "Rejected"
#campaign.save(validate: false)
end
end
end
def approve
#campaign = Campaign.find(params[:id])
params[:campaign].each do |key, value|
if value[:status] = "Approved"
#applied_campaign = AppliedCampaign.new(:campaign_id => key, :start_date => value[:start_date]||Time.now, :end_date => value[:end_date], :active => true)
end
end
end
The problem is when control is passed to approve or reject the entire campaign string is passed with both records contained within whereas I want to seperate each record and pass it individually. Can anyone indicate why the entire campaign string is being passed?
Move the params inside the route helper
redirect_to approve_operator_campaign_path(key, param_1: 1, param_2: 2)
# Parameters: { "id"=>"2" "param_1"=>"1", "param_2"=>"2" }
Change your method to:
def process_campaign
authorize! :operator, current_user.operator
params[:campaign].each do |key, value|
if value[:status] == "Approved"
redirect_to approve_operator_campaign_path(key, id: key, start_date: value[:start_date], revenue_mode: value[:revenue_model], end_date: value[:end_date], active: true, status: 307) and return
elsif value[:status] == "Rejected"
redirect_to reject_operator_campaign_path(key, campaign_name: key, notes: value[:notes], status: 307) and return
end
end
redirect_to operator_campaigns_path, flash: { notice: "Campaigns have been processed."}
end
I am trying to search through my model using 3 columns. Also if the column is empty, it is valid. This is how I am doing it
def getactivityfortoday
#temp = params[:temp]
logger.debug "params temp:#{#temp.inspect}"
#sky = params[:sky]
#day = params[:day]
#todaysactivities = []
#activities=[]
#finaldata = []
#activities = Weatherclockactivity.all
#attemptactivities = []
#attemptactivities = #user.attempts
for activity in #activities do
logger.debug "activity: #{activity.attributes.inspect}"
if #temp.to_i < activity.temperatureMax.to_i && #temp.to_i > activity.temperatuureMin.to_i
if #sky == activity.sky || activity.sky == ""
if #day == activity.day
#todaysactivities << activity
end
end
end
end
for activity in #todaysactivities
for attempt in #attemptactivities
if attempt == activity
finaldata << {activity: activity, attempt: "yes"}
else
finaldata << {activity: activity, attempt: "no"}
end
end
end
respond_to do |format|
format.html { render action: "new" }
format.json { render json: #finaldata }
end
The response I get is an empty array but I should be getting 3 rows as a response.
spelling mistake here
activity.temperatuureMin.to_i
And
finaldata << {activity: activity, attempt: "yes"}
should be
#finaldata << {activity: activity, attempt: "yes"}
Also you could be more concise
def getactivityfortoday
#temp = params[:temp]
logger.debug "params temp:#{#temp.inspect}"
#sky = params[:sky]
#day = params[:day]
#activities = Weatherclockactivity.all
#attemptactivities = #user.attempts
#finaldata = #activities.map do |activity|
if (activity.temperatureMin.to_i + 1...activity.temperatureMax.to_i).include?(#temp.to_i) && ( #sky == activity.sky || activity.sky == "") && #day
#attemptactivities.include?(activity) ? {activity: activity, attempt: "yes"} : {activity: activity, attempt: "no"}
end
end.compact
respond_to do |format|
format.html { render action: "new" }
format.json { render json: #finaldata }
end
end
How about something like this?
I tried to make it a balance of readability and conciseness. First we filter for the desired activities. Then we structure the output. This should be easier to debug.
def getactivityfortoday
#temp = params[:temp].to_i
#sky = params[:sky]
#day = params[:day]
#activities = Weatherclockactivity.all
#attemptactivities = #user.attempts
selected_activities = #activities.select do |activity|
# Make sure it's the right temperaure
return false unless (activity.temperatureMin.to_i + 1 ... activity.temperatureMax.to_i).include? #temp
# Make sure the sky matches, or the sky is blank
return false unless (#sky.blank? || #sky.activity == activity.sky)
# Make sure the day matches
return false unless #day == activity.day
# Otherwise, it's good!
return true
end
selected_attempted_activities = selected_activities.map do|activity|
ret = {activity: activity}
ret[:attempt] = #attemptactivities.include?(activity) ? "yes" : "no"
ret
end
respond_to do |format|
format.html { render action: "new" }
format.json { render json: selected_attempted_activities }
end
end
There are a few typos in your original (for instance, #finaldata not finaldata). Make sure that you spell instance variables (things starting with #, like #sky) correctly, since if you try to access an undefined instance variable, it'll silently default to nil.
The best and flexible way is to use ActiveModel::Model
It allows you to use many more useful methods.
it will seems like:
app/models/activity_report.rb
Class ActivityReport
include ActiveModel::Model
attr_accessor :day, :activity # and etc.
validates :day, presence: true
def day
#day.to_s # for example
end
def day=(value)
#day = value - 1.month # for example every date which user set will set on one month ago
end
# and etc
end
app/controllers/posts_controller.rb
...
def index
#activity = ActivityReport.new(params[:activity])
end
def create
#activity.create!
end
...
app/views/posts/index.html.haml
= form_for #activity do |f|
= f.day
For more information you could take a look at:
http://edgeapi.rubyonrails.org/classes/ActiveModel/Model.html
http://railscasts.com/episodes/219-active-model (old)
http://railscasts.com/episodes/416-form-objects (newer, but a little complex)
In my code,i am parsing a JSON object like
[{"name":"karthi"},{"name":"shreshtt"},{"name":"jitu"},{"name":null},{"name":null},{"name":null},{"name":null}]
In this, I want to collect all names in an single array object. This is how my controller looks as of now. I want to store the resultant name array in #hotels variable.
controller.erb
respond_to :json, :xml
def index
#hotels = Hotel.all
respond_to do |format|
format.html # show.html.erb
format.json { render json: #hotels.to_json(:only => [ :name ]) }
end
end
view/hoels/index.json.erb
[
hotel: <% #hotels.each do |hotel| %>
{ 'name': "<%= hotel.name.to_json.html_safe %>" }
<% unless index== #hotels.count - 1%>
<% end %>
<% end %>
]
You want to add just the names to an array?
How about:
a = [{name: "karthi"},{name: "shreshtt"},{name: "jitu"},{name: nil},{name: nil},{name: nil},{name: nil}]
#hotel = []
a.collect{|a_name| #hotel << a_name[:name]}
=> ["karthi", "shreshtt", "jitu", nil, nil, nil, nil]
#hotel.compact!
=> ["karthi", "shreshtt", "jitu"]
What´s about that?
a = {}
a["hotel"] = []
array = [{"name"=>"kathi"}, {"name"=>"kathi2"}, {"name"=>"kathi3"}, {"name"=>"kathi4"}, {"name" => nil}]
a["hotel"] = array
a["hotel"].each do |v|
if v["name"] == nil
a["hotel"].delete(v)
end
end
a => {"hotel"=>[{:name=>"kathi"}, {:name=>"kathi2"}, {:name=>"kathi3"}, {:name=>"kathi4"}]}
You can do like following
hotels = Hotel.select("name").where("name is not NULL")
json_obj = {hotels: hotels}.to_json
format.json { render json: json_obj }
In my controller I have:
def search
#sog = Konkurrencer.where("titel like ?", "%#{params[:q]}%")
#kate = []
#sog.each do |kat|
h = {}
kat.attributes.each{|k,v| h[k] = v.respond_to?(:force_encoding) ? v.dup.force_encoding("UTF-8") : v }
#kate << h
end
respond_to do |format|
format.html
format.json { render :json => #kate }
end
The problem is that the JSON contains all the attributes for the model. How do I create a JSON that have only ID, url and titel?
The JSON should also contain the key "url" which key should be the URL for the associated photo. I use paperclip. The path is: #konkurrencer.photo.image.url
UPDATE:
My search.json.erb:
[
<% #sog.each do |kon| %>
{"id":"<%= kon.id %>","titel":"<%= kon.titel %>"},
<% end %>
]
How do I remove the , for the last loop?
Create an array with the list of attributes you want to display. Use select query method to get only this fields in the SQL request. And finally loop on this attributes to fill the JSON array:
def search
displayed_attributes = %w{id url titel}
#sog = Konkurrencer.select(displayed_attributes.join(',')).where("titel like ?", "%#{params[:q]}%")
#kate = []
#sog.each do |kat|
h = {}
displayed_attributes.each do |attribute|
v = kat[attribute]
h[attribute] = v.respond_to?(:force_encoding) ? v.dup.force_encoding("UTF-8") : v
end
#kate << h
end
respond_to do |format|
format.html
format.json { render :json => #kate }
end
end