Lets say I wish to make a page that can query the desired object with type(string) and id(int).
/query?type=people&id=1
would fetch me
Person.find(1)
whereas
/query?type=cities&id=123
would fetch me
City.find(123)
However, I have problems as to how to translate strings into the desired model class.
The only way I can think of is
case params[:type]
when 'people'
#object = Person.find(params[:id])
when 'cities'
#object = City.find(params[:id])
end
However, this method will be quite problematic if I have more types of models.
Is there a better way?
Thank you in advance,
Try:
klass = params[:type]
klass.singularize.classify.constantize.find(params[:id])
Edit:
#object = params[:type].singularize.classify.constantize.find(params[:id])
Related
I created a class method that is called when a new object is created and copied from an old existing object. However, I only want to copy some of the values. Is there some sort of Ruby shorthand I can use to clean this up? It's not entirely necessary, just curious to know if something like this exists?
Here is the method I want to DRY up:
def set_message_settings_from_existing existing
self.can_message = existing.can_message
self.current_format = existing.current_format
self.send_initial_message = existing.send_initial_message
self.send_alert = existing.send_alert
self.location = existing.location
end
Obviously this works perfectly fine, but to me looks a little ugly. Is there any way to clean this up? If I wanted to copy over every value that would be easy enough, but because I only want to copy these 5 (out of 20 something) values, I decided to do it this way.
def set_message_settings_from_existing(existing)
[:can_message, :current_format, :send_initial_message, :send_alert, :location].each do |attribute|
self.send("#{attribute}=", existing.send(attribute))
end
end
Or
def set_message_settings_from_existing(existing)
self.attributes = existing.attributes.slice('can_message', 'current_format', 'send_initial_message', 'send_alert', 'location')
end
a hash might be cleaner:
def set_message_settings_from_existing existing
fields = {
can_message: existing.can_message,
current_format: existing.current_format,
send_initial_message: existing.send_initial_message,
send_alert: existing.send_alert,
location: existing.location
}
self.attributes = fields
end
you can take this further by only selecting the attributes you want:
def set_message_settings_from_existing existing
fields = existing.attributes.slice(
:can_message,
:current_format,
:send_initial_message,
:send_alert,
:location
)
self.attributes = fields
end
at this point you could also have these fields defined somewhere, eg:
SUB_SET_OF_FIELDS = [:can_message, :current_format, :send_initial_message, :send_alert, :location]
and use that for your filter instance.attributes.slice(SUB_SET_OF_FIELDS)
What i do wrong? I want to return every products which pass condition:
def show
products = Product.select{|x| x[:category_id] == params[:id]}
render json: products
end
When i write
def show
products = Product.select{|x| x[:category_id] == 1}
render json: products
end
it works why the first example doesn't work?
I am pretty sure that there is mismatch in data type.
1=='1' #will be always false
1==1 #will be true
'1'=='1' #will be true as well
And also check for nil value from params[:id]
Please make sure to change as follows
def show
products = Product.select{|x| x.category_id == params[:id].to_i}
render json: products
end
OR
The best solution as suggested by #Eyeslandic is to use .where as it will not check for mismatch in data type. And also you don't have to take care of nil value from params[:id].
You should really be using a where to stop sql from loading all your products.
#products = Product.where('category_is = ?', params[:id])
The being said, if you are sticking to rails restful conventions, the fact you have a param called :id that is the category_id suggests you are on the category controller. So maybe consider changing your logic to:
#category = Category.includes(:products).find(params[:id])
you can then access products via
#category.products
or if your not interested in the category too much maybe
#products = Category.includes(:products).find(params[:id])&.products
I am trying to do a search with multiple attributes for Address at my Rails API.
I want to search by state, city and/or street. But user doesn't need to send all attributes, he can search only by city if he wants.
So I need something like this: if the condition exists search by condition or return all results of this condition.
Example:
search request: street = 'some street', city = '', state = ''
How can I use rails where method to return all if some condition is nil?
I was trying something like this, but I know that ||:all doesn't work, it's just to illustrate what I have in mind.:
def get_address
address = Adress.where(
state: params[:state] || :all,
city: params[:city] || :all,
street: params[:street] || :all)
end
It's possible to do something like that? Or maybe there is a better way to do it?
This is a more elegant solution using some simple hash manipulation:
def filter_addesses(scope = Adress.all)
# slice takes only the keys we want
# compact removes nil values
filters = params.permit(:state, :city, :street).to_h.compact
scope = scope.where(filters) if filters.any?
scope
end
Once you're passing a column to where, there isn't an option that means "on second thought don't filter by this". Instead, you can construct the relation progressively:
def get_address
addresses = Address.all
addresses = addresses.where(state: params[:state]) if params[:state]
addresses = addresses.where(city: params[:city]) if params[:city]
addresses = addresses.where(street: params[:street]) if params[:street]
addresses
end
I highly recommend using the Searchlight gem. It solves precisely the problem you're describing. Instead of cluttering up your controllers, pass your search params to a Searchlight class. This will DRY up your code and keep your controllers skinny too. You'll not only solve your problem, but you'll have more maintainable code too. Win-win!
So in your case, you'd make an AddressSearch class:
class AddressSearch < Searchlight::Search
# This is the starting point for any chaining we do, and it's what
# will be returned if no search options are passed.
# In this case, it's an ActiveRecord model.
def base_query
Address.all # or `.scoped` for ActiveRecord 3
end
# A search method.
def search_state
query.where(state: options[:state])
end
# Another search method.
def search_city
query.where(city: options[:city])
end
# Another search method.
def search_street
query.where(street: options[:street])
end
end
Then in your controller you just need to search by passing in your search params into the class above:
AddressSearch.new(params).results
One nice thing about this gem is that any extraneous parameters will be scrubbed automatically by Searchlight. Only the State, City, and Street params will be used.
#matched = [1, 2, 3]
Where each integer represents the id of an ActiveRecord object in the Inventory class. As a next step, I want to look at each of those objects and obtain the email of the parent User, but I'm not sure how to do it. Ideally I'd write something like:
Inventory.where(id: #matched).user.email
Because certainly, this statement would work if I only had a single id to look up. Since that doesn't work, I'm instead doing this
#email = []
#matched.each do |i|
#email << Inventory.find_by_id(i).user.email
end
Just wondering if there's an easier way.
Thanks!
If you only need the email addresses then you can use the pluck method:
Inventory.where(id: #matched).joins(:user).pluck("users.email")
class Inventory
def self.with_ids(ids)
sql = #matched.map{|id| "id = #{id}"}.join(" OR ")
where(sql)
end
def parent_email
user.email
end
end
Inventory.with_ids(#matched).map(&:parent_email)
I'm trying to delete a single instance from a database query. "l.remove" represents what i want to do but i know its wrong. I have tried delete and destroy. destroy didn't work and delete actually removed the data from the database. I just want the data removed from the variable. Can anyone help me?
<%
#owner = User.find(params[:id])
#job_list = ShoppingList.where(:user_id=>#user.user_id)
#job_list.each do |l|
#temp = FlaggedCandidate.where(:flagged_user_id=>#owner.user_id, :list_id=>l.list_id)
if !#temp.nil?
l.remove
end
end
#candidate = FlaggedCandidate.new
%>
based on the code i assume that User has many ShoppingList.
You can do something like:
#job_list = #owner.shopping_lists.where( list_id: FlaggedCandidate.where( flagged_user_id: #owner.user_id ).pluck(:list_id) )
That could save the trouble of looping around.
You are trying to remove record from db. In order to just modify collection #job_list you need reject some unsatisfied elements. You can do it with select method (to select job_lists that flagged), or reject in opposite. This is how you code should looks like:
#owner = User.find(params[:id])
#job_list = ShoppingList.where(:user_id=>#user.user_id)
#job_list.select! do |job_list|
FlaggedCandidate.where(
:flagged_user_id => #owner.user_id,
:list_id => job_list.list_id
).any?
end
#candidate = FlaggedCandidate.new
select! simply change the original collection, instead of doing #job_list = #job_list.select { ... }