Rails - How to use find or create with a default value - ruby-on-rails

I currently have the following:
conversation_participation = #user.conversation_participations.find_or_create_by_conversation_id(conversation.id)
This correctly creates a record, problem is the default value of conversation.read is false.
In this particular method I want the default value to be true when creating the record. Right now the only way I got this to work was by the following:
conversation_participation = #user.conversation_participations.find_or_create_by_conversation_id(conversation.id)
conversation_participation.read = true
conversation_participation.save
Problem is this hits the DB twice. How can I use find_or_create and set the default :read => true?
Thanks

You can try find_or_initialize_by...
Then set your read attribute as normal
conversation_participation = #user.conversation_participations.find_or_initialize_by_conversation_id(conversation.id)
conversation_participation.read = true
conversation_participation.save
Or
conversation_participation = #user.conversation_participations.find_or_initialize_by_conversation_id_and_read(conversation.id, true)
conversation_participation.save
With after_initialize (Oh.. you deleted your comment, but here is something for that just in case)
class Conversation < ActiveRecord::base
def after_initialize
self.read ||= true # true if not already set
end
end
Then you can do find_or_create|initialize_by... or which ever way you wish to proceed.
More on callbacks if you are interest.

From here:
Use the find_or_initialize_by_
finder if you want to return a new
record without saving it first.
So something like this:
conversation_participation = #user.conversation_participations.find_or_initialize_by_conversation_id(conversation.id)
conversation_participation.read = true
conversation_participation.save
This should just do an INSERT instead of an INSERT followed by an UPDATE.

Try this:
conversation_participation = #user.conversation_participations.find_or_create_by_conversation_id_and_read(conversation.id, true)

Related

Initializing a boolean on a form object that can be changed

I have a form object that I want to have an enabled field on which can be true or false depending on other logic in my project, how do I initialize something like that in the form object class? I have something like this...
attr_accessor :enabled
def initialize
self.enabled = enabled
But I know this is incorrect.
And then in another spot in my code I want to do something like this
if something
form_object.enabled
elsif something_else
!form_object.enabled
end
Setting that enabled flag to the appropriate state so that I can use it for later
Edit: adding more info
The place where I want to put this enabled variable is in a helper Ruby Class I have
module Blah
module Type
module Mixins
module CallToActionHelper
attr_accessor :enabled
def initialize(cta_use: nil, cta: nil)
self.cta_type = cta_type
self.cta_token = create_cta_token
self.enabled = enabled
And then I have different ActiveRecord models for each type of call to action I have. They all also use the same controller, and the controller is the place where that enabled variable will be set depending on a hidden field in my params.
def populate_form_object(form_params, form_token)
cta_use = set_call_to_action_use(cta_use_id: form_params['cta_use_id'], cta_type_key: form_params['cta_type_key'], cta_id: form_params['cta_id'])
cta_type = CallToActionType.find_by_key(form_params['cta_type_key'])
form_object = cta_type.get_type_form(cta_use: cta_use)
form_object.cta_token = form_token
form_object.enabled(params["call_to_action_use_#{form_token}_enabled"])
# if params["call_to_action_use_#{form_token}_enabled"]
# form_object.enabled = true
# elsif params["call_to_action_use_#{form_token}_enabled"] == 'false'
# form_object.enabled = false
# else
# form_object.enabled = true
# end
return form_object
end
You can see the code I've commeneted out where I tried to set this enabled field.
The above method, populate_form_object gets called on a save.

Update fails first time, succeeds second time

We've got this object, #current_employer, that's acting a bit weird. Update fails the first time, succeeds the second.
(byebug) #current_employer.update(settings_params)
false
(byebug) #current_employer.update(settings_params)
true
Here's where we initialise it:
#current_employer = Employer.find(decoded_auth_token[:employer_id])
It's just a standard "find".
Current workaround:
if #current_employer.update(settings_params) || #current_employer.update(settings_params)
...
Anyone seen this before?
Update
Tracked it down to this line in a "before_save" call
# self.is_test = false if is_test.nil?
Seems like is_test is a reserved keyword?
Solved
The full callback, with the fix commented inline:
def set_default_values
self.has_accepted_terms = false if has_accepted_terms.nil?
self.live = true if live.nil?
self.account_name.downcase!
self.display_name ||= account_name
self.display_name ||= ""
self.contact_first_name ||= ""
self.contact_last_name ||= ""
self.phone_number ||= ""
self.is_test_account = false if is_test_account.nil?
true # FIX. The proceeding line was returning 'false', which was giving us a 'false' return value for the before_save callback, preventing the save.
end
Model
If it's failing in one instance and succeeding almost immediately afterwards, the typical issue is that you're passing incorrect / conflicting attributes to the model.
I would speculate that the settings_params you're sending have a value which is preventing the save from occurring. You alluded to this with your update:
# self.is_test = false if is_test.nil?
The way to fix this is to cut out any of the potentially erroneous attributes from your params hash:
def settings_params
params.require(:employer).permit(:totally, :safe, :attributes)
end
Your model should update consistently - regardless of what conditions are present. If it's failing, it means there'll be another problem within the model save flow.
--
Without seeing extra information, I'm unable to see what they may be
Update
A better way to set default values is as follows:
How can I set default values in ActiveRecord?
You may wish to use the attribute-defaults gem:
class Foo < ActiveRecord::Base
attr_default :age, 18
attr_default :last_seen do
Time.now
end
end

How do I define a static value for a parameter of a new class?

I created a model for a thing called a Lesson that has :content and a :user_id. For early builds of this app, I want the content to be changing, based on my entry, and for the user_id to always = 1 so that it's clean in the DB and there's not a nil value.
How do I go about this?
In my lessons_controller.rb I have this:
def create
#lesson = Lesson.new(params[:lesson])
if #lesson.save
... do something
else
... do something else
I'm guessing this would be the best place to define that the user_id = 1 but how should I go about that?
You can just set #lesson.user_id = 1 in the line after you create it with new, and before you save it.
Another way to do it would be to set a hook in the lesson model -
before_validation :on => :create do |lesson|
lesson.user_id = 1
end

Rails How can one query association definitions

I have a lot of dynamic code which keeps complex relations in a string.
ex:
"product.country.continent.planet.galaxy.name"
How can I check if these relations exist?
I want a method like the following:
raise "n00b" unless Product.has_associations?("product.country.planet.galaxy")
How could I implement this?
Try this:
def has_associations?(assoc_str)
klass = self.class
assoc_str.split(".").all? do |name|
(klass = klass.reflect_on_association(name.to_sym).try(:klass)).present?
end
end
If these are active record associations, here's how you can do it:
current_class = Product
has_associations = true
paths = "country.planet.galaxy".split('.')
paths.each |item|
association = current_class.reflect_on_association( item )
if association
current_class = association.klass
else
has_associations = false
end
end
puts has_association
And this will tell you if this specific path has all the associations.
If indeed you are storing the AR associations in a string like that, this code placed in an initializer should let you do what you want. For the life of me I can't quite figure out why you'd want to do this, but I trust you have your reasons.
class ActiveRecord::Base
def self.has_associations?(relation_string="")
klass = self
relation_string.split('.').each { |j|
# check to see if this is an association for this model
# and if so, save it so that we can get the class_name of
# the associated model to repeat this step
if assoc = klass.reflect_on_association(j.to_sym)
klass = Kernel.const_get(assoc.class_name)
# alternatively, check if this is a method on the model (e.g.: "name")
elsif klass.instance_method_already_implemented?(j)
true
else
raise "Association/Method #{klass.to_s}##{j} does not exist"
end
}
return true
end
end
With this you'll need to leave off the initial model name, so for your example it would be:
Product.has_associations?("country.planet.galaxy")

Rails - Exclude an attribute from being saved

I have a column named updated_at in postgres. I'm trying to have the db set the time by default. But Rails still executes the query updated_at=NULL. But postgres will only set the timestamp by default when updated_at is not in the query at all.
How do I have Rails exclude a column?
You can disable this behaviour by setting ActiveRecord::Base class variable
record_timestamps to false.
In config/environment.rb, Rails::Initializer.run block :
config.active_record.record_timestamps = false
(if this doesn't work, try instead ActiveRecord::Base.record_timestamps = false at the end of the file)
If you want to set only for a given model :
class Foo < ActiveRecord::Base
self.record_timestamps = false
end
Credit to Jean-François at http://www.ruby-forum.com/topic/72569
I've been running into a similar issue in Rails 2.2.2. As of this version there is an attr_readonly method in ActiveRecord but create doesn't respect it, only update. I don't know if this has been changed in the latest version. I overrode the create method to force is to respect this setting.
def create
if self.id.nil? && connection.prefetch_primary_key?(self.class.table_name)
self.id = connection.next_sequence_value(self.class.sequence_name)
end
quoted_attributes = attributes_with_quotes(true, false)
statement = if quoted_attributes.empty?
connection.empty_insert_statement(self.class.table_name)
else
"INSERT INTO #{self.class.quoted_table_name} " +
"(#{quoted_attributes.keys.join(', ')}) " +
"VALUES(#{quoted_attributes.values.join(', ')})"
end
self.id = connection.insert(statement, "#{self.class.name} Create",
self.class.primary_key, self.id, self.class.sequence_name)
#new_record = false
id
end
The change is just to pass false as the second parameter to attributes_with_quotes, and use quoted_attributes.keys for the column names when building the SQL. This has worked for me. The downside is that by overriding this you will lose before_create and after_create callbacks, and I haven't had time to dig into it enough to figure out why. If anyone cares to expand/improve on this solution or offer a better solution, I'm all ears.

Resources