How can I refactor a simple dynamic attribute? - ruby-on-rails

I have a form that handles four different types of facets of the same form. In my SQL column, I have the four different attributes.
Only one of them is going to have data in it.
Distribution =>
zip_code: nil
me_topic: nil
sex: nil
age: nil
In order to differentiate between them, I wanted to set up a case statement, and add a dynamic attribute to the create call :
#type = case params[:type]
when "zip" then ":zip_code"
when "interest" then ":me_topic"
when "sex" then ":sex"
when "age" then ":age"
end
#cur_item = Distribution.new(#type => params[:value])
# Unfortunately, this is not the proper way to create a dynamic attribute
#distribution = #email.distributions.create(params[:distributions])
What is the proper syntax for completing this statement?

Declare a method called type_map
def type_map params
##type_map ||= {
"zip" => :zip_code,
"interest" => :me_topic,
"sex" => :sex,
"age" => :age
}
{ ##type_map[params[:type]] => params[:value]
end
Now you can use the map as follows:
#distribution = #email.distributions.create(type_map(params))

This is what I went with, but feel free to best my answer.
#cur_item = case params[:type]
when "zip" then {:zip_code => params[:value]}
when "interest" then {:me_topic => params[:value]}
when "sex" then {:sex => params[:value]}
when "age" then {:age => params[:value]}
end
#distribution = #email.distributions.create(#cur_item)

Well, one way to improve your code (what you posted in your answer) would be to factor out the repeated params[:value] as follows:
key = case params[:type]
when "zip" then :zip_code
when "interest" then :me_topic
when "sex" then :sex
when "age" then :age
end
#cur_item = { key => params[:value] }
#distribution = #email.distributions.create #cur_item

Related

How do I access a local variable in Active Record query with SQL

I'm trying some code to find an attribute with a time value that is less than current time.
If I have current_time = Time.now, how do I find it using where such as:
Outage.where("end_time < current_time") # this doesn't work.
There are many ways to do that via placeholder.
Using ? Placeholder
You can use ? as a placeholder in a query condition.
User.where('users.name = ?', 'John')
With multiple placeholders:
User.where('users.name = ? AND users.last_name = ?', 'John', 'Smith')
Using Named Placeholder
User.where('first_name = :first_name', { :first_name => 'John' })
With multiple placeholder:
values = { :first_name => 'John', :last_name => 'Smith'}
conditions = 'first_name = :first_name AND last_name = :last_name'
User.where(conditions , values)
Noted that order does not matters. The following code would work correctly since we have already named those placeholders.
values = { :last_name => 'Smith', :first_name => 'John'}
conditions = 'first_name = :first_name AND last_name = :last_name'
User.where(conditions , values)
References: Using Named Placeholders in Ruby
You can use placeholder - ? and then pass the value, like this:
Outage.where('outages.end_time < ?', current_time)

How to combine two as_json methods to one correctly?

In my Model I have a working as_json method as follows:
def as_json(options = {})
super(options.merge(include: [:user, comments: {include: :user}]))
end
This method is for including users in comments.
Now I need to add almost the same thing in the same model for answers:
def as_json(options = {})
super(options.merge(include: [:user, answers: {include: :user}]))
end
How do I combine these two as_json methods, so that I have one as_json method?
Don't laugh but I am struggling with this for 3 days.
This is one of the reasons why you should not use the built-in to_json to serialize ActiveRecord models.
Instead, you should delegate the task to another object called serializer. Using a serializer allows you to have illimitate representations (serializations) of the same object (useful if the object can have different variants such as with/without comments, etc) and separation of concerns.
Creating your own serializer is stupid simply, as simple as having
class ModelWithCommentsSerializer
def initialize(object)
#object = object
end
def as_json
#object.as_json(include: [:user, comments: {include: :user}]))
end
end
class ModelWithAnswersSerializer
def initialize(object)
#object = object
end
def as_json
#object.as_json(include: [:user, answers: {include: :user}]))
end
end
Of course, that's just an example. You can extract the feature to avoid duplications.
There are also gems such as ActiveModelSerializers that provides that feature, however I prefer to avoid them as they tend to provide a lot of more of what most of users really need.
Why are you trying to override core Rails functionality - not good practice unless absolutely necessary.
--
This says the following:
To include associations use :include:
user.as_json(include: :posts)
# => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
# "created_at" => "2006/08/01", "awesome" => true,
# "posts" => [ { "id" => 1, "author_id" => 1, "title" => "Welcome to the weblog" },
# { "id" => 2, "author_id" => 1, "title" => "So I was thinking" } ] }
You could call:
#answers.as_json(include :users)
--
Ohhhhhhhh:
Second level and higher order associations work as well:
user.as_json(include: { posts: {
include: { comments: {
only: :body } },
only: :title } })
# => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
# "created_at" => "2006/08/01", "awesome" => true,
# "posts" => [ { "comments" => [ { "body" => "1st post!" }, { "body" => "Second!" } ],
# "title" => "Welcome to the weblog" },
# { "comments" => [ { "body" => "Don't think too hard" } ],
# "title" => "So I was thinking" } ] }
So looks like you could call:
#answers.to_json(include: comments: { include: :users })
def as_json(other_arg, options = {})
as_json(options.merge(include: [:user, other_arg: {include: :user}]))
end
And then you can call:
MyModel.as_json(:comments)
MyModel.as_json(:answers)

Best way to join two different model queries in Rails?

I have two different models that I need to join together in a selection query and list on a page. They share all of attributes that I'll need to reference in the view (created_at, updated_at, name, etc), and I want them in order of creation. I'm wondering what the most efficient way to do this is? I was thinking of performing the selection query on each object individually and adding the relevant parts into a common array but that seems inefficient.
For example if my models were Dogs and Cats and I wanted a list of all dogs and cats of age 5, I was thinking something like
#pets = []
dogs = Dogs.where(:age => 5)
cats = Cats.where(:age => 5)
dogs.each do |dog|
hash = {"id" => dog.id, "name" => dog.name, "created_at" => dog.created_at }
#pets.push(hash)
end
cats.each do |cat|
hash = {"id" => cat.id, "name" => cat.name, "created_at" => cat.created_at }
#pets.push(hash)
end
But is that the best way to do it? also, I'm not sure how to sort the finished array in this example according to date created...
try this
dogs = Dogs.where(:age => 5)
cats = Cats.where(:age => 5)
#pets = (dogs + cats).sort_by { |pet| pet.created_at }
OR if you want your hashes still, use map to create the array of hashes
dogs = Dogs.where(:age => 5)
cats = Cats.where(:age => 5)
#pets = (dogs + cats).sort_by do |pet|
pet.created_at
end.map do |pet|
{ "id" => dog.id, "name" => pet.name, "created_at" => pet.created_at }
end

Where to put constants in Rails

I have a few constants which are arrays that I don't want to create databse records for but I don't know where to store the constants without getting errors.
For example
CONTAINER_SIZES = [["20 foot"],["40 foot"]]
Where can I store this so all models and controller have access to this?
I will write my way to you.
class User < ActiveRecord::Base
STATES = {
:active => {:id => 100, :name => "active", :label => "Active User"},
:passive => {:id => 110, :name => "passive", :label => "Passive User"},
:deleted => {:id => 120, :name => "deleted", :label => "Deleted User"}
}
# and methods for calling states of user
def self.find_state(value)
if value.class == Fixnum
Post::STATES.collect { |key, state|
return state if state.inspect.index(value.to_s)
}
elsif value.class == Symbol
Post::STATES[value]
end
end
end
so i can call it like
User.find_state(:active)[:id]
or
User.find_state(#user.state_id)[:label]
Also if i want to load all states to a select box and if i don't want some states in it (like deleted state)
def self.states(arg = nil)
states = Post::STATES
states.delete(:deleted)
states.collect { |key, state|
if arg.nil?
state
else
state[arg]
end
}
end
And i can use it now like
select_tag 'state_id', User.states.collect { |s| [s[:label], s[:id]] }
I put them directly in the model class.
class User < ActiveRecord::Base
USER_STATUS_ACTIVE = "ACT"
USER_TYPES = ["MANAGER","DEVELOPER"]
end

Making an if statement inside a hash in a model

i am trying to display all the residents on a pdf and their dependents, but the dependents doesn't have a stand they are identified by the foreign key user_id of the resident. e.g resident1.id = 5 --- dependent3.user_id = 5 mean dependent3 belongs to resident1, and therefore if i want to display the dependent stand what should i do and i would like to display all the residents and their dependents and the stand information for dependents to be the stand info of the resident the dependent belong to. now my information should be inside a hash so it can generate my pdf file..
my code is
data = []
c = 1
residents.each do |r|
data <<{"Fullname" => r.firstname, "Lastname" => r.lastname, "Street-Number" => r.stand.street_no, "street Name" => r.stand.streetname} if r.stand || r.user_id = r.id
end
remember my dependents and residents are in the same table but residents doesn't have the user_id foreigh key only the dependent have it.
and my output only display the information of the residents who have stands not the dependents.
please anyone who is willing to help.cause i dont know if i can but an if statement inside a hash like:
residents.each do |r|
data <<{"Fullname" => r.firstname, "Lastname" => r.lastname} if r.stand || r.user_id = r.id{"Street-Number" => r.stand.street_no, "street Name" => r.stand.streetname}
Assuming that .stand.street_no, etc. is valid even if .stand is false (since it could be false and still be called if the r.user_id == r.id part is true), then the following would work:
data = residents.map do |r|
hash = { "Fullname" => r.firstname, "Lastname" => r.lastname }
hash.merge!({ "Street-Number" => r.stand.street_no, "street Name" => r.stand.streetname}) if r.stand || r.user_id == r.id
hash
end
Explanation:
Iterate through each resident object and build a hash that contains values that we know will be used.
Conditionally merge! in the extra keys if the if clause is true.
Return the hash.
Doing this in a map eliminates the need to predeclare the data array, nor push the hash on each pass through the loop.
data = []
residents.each do |r|
tmp_hash = {"Fullname" => r.firstname, "Lastname" => r.lastname}
if r.stand || r.user_id = r.id
tmp_hash.merge({"Street-Number" => r.stand.street_no, "street Name" => r.stand.streetname})
end
data << tmp_hash
end

Resources