How to refactor this code for more DRY controller?
#places = Place
#places = #places.address(params[:address]) if params[:address].present?
#places = #places.address(params[:name]) if params[:name].present?
#places = #places.price_greater_than_equal params[:price_from] if params[:price_from].present?
#and more..
method address is build from scope in model
sorry for bad english
Why are you not using the Rails Way ? if params are present they will be updated. the validations should be in the model in necessary.
def update
#places.update(places_params)
respond_with(#places)
end
private
def set_places
#places = Places.find(params[:id])
end
def places_params
params.require(:places_params).permit(:address, :postcode, address_attributes: :address (nested attributes))
end
Related
class Contact < ActiveRecord::Base
attr_accessor :email
def initialize(data)
data.each { |k, v| send("#{k}=", v) }
end
end
In rails console
Contact.create!({"email"=>"foo#gmail.com"})
The record saved to the database has email as nil
Update:
The data is being passed in is JSON. I am getting all the data from the JSON and trying to save that into the database.
Did you try:
Contact.create!(email: "foo#gmail.com")
The email as a :symbol and no curly brackets?
Also, why are you initializing in your model?
With Mohamed El Mahallaway, I think your code setup could be improved (to negate initializing your model). I think you'll be better using the strong_params functionality of Rails:
#app/controllers/contacts_controller.rb
def new
#contact = Contact.new
end
def create
#contact = Contact.new(contact_params)
#contact.email = "foo#gmail.com"
#contact.save
end
private
def contact_params
params.require(:contact).permit(:email, :other, :params)
end
I may have miscalculated your competency with Rails, but this is the "Rails" way to save the correct data to your model :) You may to have a before_save method in your model to use the email attribute, considering it's a virtual attribute
I would like to remove this logic:
Suitcase::Hotel.find(id: hotel.id).images.first.url
from view.
https://gist.github.com/2719479
I dont have model Hotel. I get this url via API using Suitcase gem.
Problem is because
hotel is from #hotels = Suitcase::Hotel.find(location: "%#{headed}%") and API recevie me images only if do Suitcase::Hotel.find(id: hotel.id)
If Suitcase::Hotel.find(id: hotel.id).images.first.url works then i would guess hotel.images.first.url will work too if hotel is an hotel instance.
Is adding:
#hotel = Suitcase::Hotel.find(id: hotel.id)
to #show action doesn't work?
EDIT:
In that case make an helper:
def hotel_image_url(hotel)
Suitcase::Hotel.find(id: hotel.id).images.first.url
end
But as I can see here you can simply write in controller:
#hotels_data = Suitcase::Hotel.find(ids: #hotels.map(&:id))
Or to be more elegant add to your model (or create decorator (it's better option)):
def photo
Suitcase::Hotel.find(id: self.id).images.first.url
end
I think this should work, not sure about the second option
class Search < ActiveRecord::Base
attr_accessible :headed, :children, :localization, :arriving_date, :leaving_date, :rooms, :adults
def hotels
#hotels ||= find_hotels
end
private
def find_hotels
return unless headed.present?
#hotels = Suitcase::Hotel.find(location: "%#{headed}%")
#hotels.each do |hotel|
def hotel.image_url
Suitcase::Hotel.find(id: hotel.id).images.first.url
end
end
end
end
# or this, but I'm not sure if this works
#hotels.each do |hotel|
image_url = Suitcase::Hotel.find(id: hotel.id).images.first.url
def hotel.image_url
image_url
end
end
In my Rails 3.1.1 project I have an ActiveModel that talks to API (ripped from Paul Dix's book, shortened for readability):
class Job
include ActiveModel::Validations
include ActiveModel::Serializers::JSON
ATTRIBUTES = [ :id,
:title,
:description,
:company_id ]
attr_accessor *ATTRIBUTES
validates_presence_of :title, :description
validates_numericality_of :company_id, :id
def initialize(attributes = {})
self.attributes = attributes
end
def attributes
ATTRIBUTES.inject(
ActiveSupport::HashWithIndifferentAccess.new
) do |result, key|
result[key] = read_attribute_for_validation(key)
result
end
end
def attributes=(attrs)
attrs.each_pair {|k, v| send("#{k}=", v)}
end
def read_attribute_for_validation(key)
send(key)
end
# More method definitions...
end
I instantiate #job in my controller, new action (company_id is a segnment key in the route: /companies/:company_id/jobs/new) like this:
#job = Job.new(company_id: params[:company_id])
Then, using CanCan, I check user's permissions to create to create a job. Basically, CanCan checks if current_user's company_id attribute matches job's company_id. This check fails because #job.company_id is returned as String.
Certainly, I can use params[:company_id].to_i while instantiating the object, but this seems like a workaround that I would have to repeat later.
Question: is there a way to make my Job ActiveModel more "type-aware" and make it return int for #job.company_id call?
I googled around, checked activemodel source code, but doesn't seem to find an answer. Any help is greatly appreciated.
Update
I was thinking more of something like schema block for ActiveModel, just like the one in ActiveResource.
attr_accessor *ATTRIBUTES
create a method like this:
def company_id
#company_id
end
You can just override that with
def company_id
#company_id.to_i
end
Answering my own question....
mosch's answer suggested to override the getter for company_id in my ActiveModel. However, I would have to repeat this for all of _id attributes in the model. Therefore, I decided to cast all of the '_id' attributes to integers while initializing the object. As follows:
def attributes=(attrs)
attrs.each_pair do |k, v|
if "#{k}".include? "_id"
send("#{k}=", v.to_i)
else
send("#{k}=", v)
end
end
end
I'm assuming your Company has_many => :jobs? If so, you could try
def new
#company = Company.find(params[:company_id])
#job = #company.jobs.new
end
I have the following (heavily simplified) model, that uses will_paginate
class Search < ActiveRecord::Base
attr_reader :articles
def after_initialize
#articles = Article.paginate_by_name name, :page => 1
end
end
and the controller code in my show action is
#search = Search.new(params[:search])
This all works fine, but notice i hard coded the page number to 1, problem is passing params[:page] value into the after_initialize method, can anyone suggest an elegant way to do this please?
Thanks
Add a page parameter (or even better an options hash parameter) to the initialize method:
class Search
def initialize(search, options = {})
#options = options
end
def after_initialize
#articles = Article.paginate_by_name name, :page => #options[:page]
end
end
and then in your controller:
#search = Search.new(params[:search], :page => params[:page])
You can even supply default values to the option hash, if you like.
I'm trying to make attributes equal predetermined values, and I'm not sure if I'm doing that efficiently with the following (in my orders controller):
def create
#order = Order.find(params[:id])
#order.price = 5.99
#order.representative = Product.find(params[:product_id]).representative
#order.shipping_location = SHIPPING_LOCATION
#order.user = current_user
respond_to do |format|
...
end
end
Is there a more efficient way to equate attributes in Rails (maybe using models)? If I'm using two different controllers, do I just repeat what I did above for the new controller?
Use before_create callback in model to assign default values.
Your code is a little off, it looks like a controller action for create, but the code reads like it's for an update.
Regardless...
You could use a parameter hash to update everything at once.
In the case where you're creating:
order_update = {:price => 5.99, :representative =>
Product.find(params[:product_id]).representative,
:shipping_location => SHIPPING_LOCATION,
:user => current_user}
#order = Order.new(order_update)
In the case where you're updating:
#order.update_attributes(order_update) #attempts to save.
Mixing it into your controller code we get:
def create
#order = Order.find(params[:id])
order_update = {:price => 5.99, :representative =>
Product.find(params[:product_id]).representative,
:shipping_location => SHIPPING_LOCATION,
:user => current_user}
respond_to do |format|
if #order.update_attributes(order_update)
# save succeeded. Redirect.
else
# save failed. Render with errors.
end
end
end
Another solution:
class Example < ActiveRecord::Base
DEFAULTS = HashWithIndifferentAccess.new(:some => 'default', :values => 'here')
def initialize(params = {})
super(DEFAULTS.merge(params))
end
end
Either use initialize and merge with params, or use an ActiveRecord hook like before_create etc.