This is very basic but not working. I want to add a callback (after_save) to upcase a field input.
In my model I have:
after_save :upcase_tax_label
def upcase_tax_label
self.tax1_label.upcase!
self.tax2_label.upcase!
end
So when I edit it should upcase the value and render in CAPS. but not. What's wrong? Thanks for your help
after_save is going to run the upcase methods after the model has already been saved to the database. In other words, it's just upcasing the object attributes in memory after the save has already completed. That's not what you want.
You want to instead use before_save so that the attributes are upcased before the object is written to the database:
before_save :upcase_tax_label
private
def upcase_tax_label
tax1_label.upcase!
tax2_label.upcase!
end
Bottom line is that you have to explicitly save a model for the changes to be made in the database. Until then, you're just playing with an object in memory.
before_save
will work. however, it's generally considered better style to write a custom setter in this situation. it would look something like this:
def tax1_label=(val)
write_attribute(:tax1_label, val.upcase)
end
def tax2_label=(val)
write_attribute(:tax2_label, val.upcase)
end
Related
I have trouble with trailblazer when setting up a simple show all Things view.
operation
class Thing < ApplicationRecord
class ShowAll < Trailblazer::Operation
include Model
model Thing, :all #why :all is not working here?
def process
end
end
end
controller
class PageController < ApplicationController
def index
run Word::ShowAll
end
end
why is :all not working for getting all Things from the db but :find works to get one via its id?
The best place to ask TRB questions is actually on Github channel.
I'm not sure where you found that example, as it is not supposed to work AFAIK, :find is a shortcut I believe, I've never actually used it.
All your logic should be defined inside the process method. http://trailblazer.to/gems/operation/1.1/api.html#process
Having said that, trying to get all records without some kind of pagination is a really bad idea, unless you are 100% sure that your table won't grow beyond several dozen records. Unless you know you don't have a large load. So to define that kind of shortcut is dangerous.
Calling Trailblazer::Model#model as you're doing there is just a shortcut for overriding the TrailBlazer::Operaration#model! method. So what you seem to want to do is:
class Thing < ApplicationRecord
class ShowAll < Trailblazer::Operation
def model!(params)
Thing.all # add any filtering or pagination here
end
end
end
And in your controller call present instead of run so that it sets up the model but doesn't call the process method of the operation.
class PageController < ApplicationController
def index
present Word::ShowAll
end
end
I'm creating an object via controller. A Foo has many Bars. The Bar model has a validation that it must have a Foo in order to be valid.
In the foo.rb model:
has_many: bars
In the bar.rb model:
validates_presence_of :foo
belongs_to: foo
In the foo_controller.rb:
#foo = Booking.new(params[:foo]) # this has all the params needed to make a Foo.
#foo.create_bars(params[:bars])
In the foo.rb model:
def create_bars(bars)
bars.each do |t|
bar = Bar.create({name: name, email: email, foo: foo})
bar.foo = self
self.bars << bar
bar.save
puts self.bars.to_s
end
end
The puts self.bars.to_s
This sounds like it should be something really basic, but since the foo doesn't exist in the db, does ActiveRecord consider it to be nil, and that's why it's not saving? How can I write this properly?
Maybe try this:
def create_bars(bars)
bars.each do |t|
self.bars << Bar.new({name: t[:name], email: t[:email]})
end
end
<< operator used for active record objects sets association automatically.
Do not forget to add has_many :foos, and belongs_to :bar in the models.
I'll answer this part first:
This sounds like it should be something really basic, but since the
foo doesn't exist in the db, does ActiveRecord consider it to be nil,
and that's why it's not saving? How can I write this properly?
Not quite. The validation doesn't depend on anything in the database, it merely checks whether the specified field (or dependent object) is present in the model before persisting. The create method is a kind-of shortcut that not only initializes an ActiveModel object, but also simultaneously attempts to save it in the database. Your validation is failing because you're calling this method BEFORE you've set the foo field of your bar object.
You can technically use create in this instance by passing foo: self in your scenario, but frankly I would go with #pablopablo89's answer in regards to this part. If you do it this way, when you save Foo, all the Bar objects will also get saved.
Additionally, the .create method for creating Bar objects is dangerous because, since you're immediately persisting Bar objects independent of the parent Foo, if for whatever reason your Foo cannot be saved (fails some other validation, etc etc), you end up with a bunch of orphaned Bar objects with no way to remove them independent of a Foo (I'm making a bit of an assumption of how your system actually works). Assuming this is a reflection of your system, the goal you want to keep in mind is that an object, and all of its dependencies, are saved in one atomic operation. If one fails, they all fail, and you alert the user.
To answer the question more generally, as #phoet pointed out in the comment, you might be a lot better off changing your form and using the accepts_nested_attributes_for method. Here is the link to the rails documentation.
In the Foo model, add the line accepts_nested_attributes_for :bars, and then use fields_for in the form, something like this:
<%= form_for #foo do |f| %>
...other stuff...
<%= f.fields_for :bars do |f_bar| %>
... bar-related stuff ...
The submitted parameter map will return with all the bar-related stuff within the :foo key in such a way that it will create both the Foo object, and all of the related Bar objects all at once. In that scenario, create will also work, although I still tend to do separate .new and .save, but its up to you.
This link regarding how to use fields_for might be helpful as well.
Booking.new just initializes an object. In order for foo to exist you need to save it. Once you save it your validation while creating bar objects will pass and the objects should be created. Hopefully this should work
I want to update an specific attribut in my database after save data.
For example i have an column in my table which called pending and it's an boolean. i want to set this value to true when data was saved.
after_save :do_something
private
def do_something
self.update_column(:pending, true)
end
This doesn't work. Anyone here who has an solution?
You can modify your attribute directly like:
after_save { |user| user.username = user.username.downcase }
Try after_commit instead of after_save. It will operate outside of the save transaction.
I am using ActiveAdmin. I need to store the User that created or updated every record.
So I added user_created_id and user_updated_id to every model.
On before_save I want to store the information about current_admin_user. The problem is that I cannot access current_admin_user in a model.
Is there a way to do that without breaking MVC?
I found this: http://rails-bestpractices.com/posts/47-fetch-current-user-in-models
But I am not sure if it is safe.
Any help?
Do it in controller (it's the reason that controller exist) before save:
def create
#my_model = MyModel.create params[:my_model]
#my_model.user_created = current_user
#my_model.save
end
The same with the update method.
I have a Post, to which a migration adds a new attribute and table column short_url. This attribute is either provided by the user, or, if left blank, automatically created:
class Post < ActiveRecord::Base
before_create :create_short_url
private
def create_short_url
if short_url.blank? || already_exists?(short_url)
write_attribute :short_url, random_string(6)
end
end
def random_string(length)
#innards are irrelevant for this question
end
end
In the migration, I want to run through all posts and have the short_url created and saved.
problem: Post.find(:all).each {|post| post.create_short_url} in the self.up is not possible, due to the private scope of the create_short_url method.
problem: Looping through posts and update!-ing them does not invoke the before_create :create_short_url, because it is not before create. Once migrated, I prefer to not have any before_update hooks in place: I don't need to change anything on update.
How would you tackle this? Copy over the random_string() and associated methods to the migration? Add specific migration helper methods to the Post?
Just use the Object method send (it doesn't check protected/private).
Post.all.each do |post|
post.send :create_short_url
post.save!
end
An alternative would be (but that could interfere with other migrations running in the same Ruby-process after that):
Post.before_save :create_short_url
Post.all.each(&:save!)
Visibility tip: Most of the time what you really mean is protected (see here). I recommend to use protected instead of private in this case.