Inserting default values in tables using rails - ruby-on-rails

Rails automatically inserts values for columns like created_at and updated_at. Can i configure rails in such a way that it updates more columns. For example all my tables have a column called user holding the currentuser value, can I default rails ti insert user for any database change?

You could try using the before_save function in your model, unless I've misunderstood the question.
before_save :defaults
def defaults
#some stuff to set your defaults
end

Yes, you could use a before_filter in the model, e.g.
before_update :set_value
def set_value
self.value = "hello"
end

You can use ActiveRecord callbacks to trigger logic when changing states, such as before saving an object to the database. The created_at and updated_at columns in automatically updated when either an object is created (before_create), or updated (before_save). You can define your own callbacks using the class methods defined in the ActiveRecord::Callbacks namespace. An example would be
# app/models/example.rb
class Example < ActiveRecord::Base
before_save :do_something
def do_something
self.value = value
end
end
If you are specifically wanting to record the user that created, updated, or deleted a record, you can save some work and use the Userstamps Rails plugin to automatically record the user. This plugin is located at https://github.com/delynn/userstamp
# app/models/example.rb
class Example < ActiveRecord::Base
model_stamper
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
include Userstamp
end
You will need to add the userstamps columns onto each of your models on which you want to record user actions.
More information on ActiveRecord callbacks can be found here: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
Information on timestamps can be found here:
ActiveRecord timestamps: http://api.rubyonrails.org/classes/ActiveRecord/Timestamp.html

Related

How to make a plain ruby object assignable as active record association

I have an Audit class which is backed by ActiveRecord.
class Audit < ActiveRecord::Base
belongs_to :user, polymorphic: true
end
I have a User class which is a plain ruby object with some ActiveModel functionality included. It's not a database model because my users are actually stored in a different database and served over an API.
class User
include ActiveModel::Conversion
include ActiveModel::Validations
extend ActiveModel::Naming
def self.primary_key
'id'
end
def id
42
end
def persisted?
false
end
end
I'm trying to assign a user to an audit like this:
audit = Audit.new
audit.user = User.new
audit.save!
From a data perspective, this should work ok. To define a polymorphic association, we need to put values into two columns on the audits table. We can set audits.user_id to the value 42 and audits.user_type to the string "User".
However, I hit an exception:
undefined method `_read_attribute' for #<User:0x007f85faa49520 #attributes={"id"=>42}> (NoMethodError)
active_record/associations/belongs_to_polymorphic_association.rb:13:in `replace_keys'
I traced that back to the ActiveRecord source and it seems to be defined here. Unfortunately, the fact that it's ActiveRecord rather than ActiveModel means that I can't include that mixin into my class.
I tried defining my own _read_attribute method but I go down a rabbit hole of having to redefine more and more ActiveRecord functionality like AttributeSet and so on.
I also realise that I can workaround the problem by assigning Audit#user_type and Audit#user_id. That is unsatisfactory however because, in reality, I would have to fork a gem and edit it to do that.
How can I modify my User class so that I can cleanly assign it to an audit.
P.S. Here's a sample app so you can try this yourself.
Instead of hacking deeper and deeper to replicate ActiveRecord functionality, you may want to consider actually inheriting from ActiveRecord::Base instead of including ActiveModel. Your only constraint is that you don't have a table. There's a gem for that:
activerecord-tableless
This class works with your sample app:
require 'active_record'
require 'activerecord-tableless'
class User < ActiveRecord::Base
has_no_table
# required so ActiveRecord doesn't try to create a new associated
# User record with the audit
def new_record?
false
end
def id
42
end
end

how to run a one-time database change on a single user

I have Customer and each customer has_many Properties. Customers belong to a Company.
I'm trying to add a certain Property to each one of a single Company's Customers. I only want this change to happen once.
I'm thinking about using a migration but it doesn't seem right to create a migration for a change that I only ever want to happen once, and only on one of my users.
Is there a right way to do this?
You can just use rails console.
In rails c:
Company.where(conditions).last.customers.each do |customer|
customer.properties << Property.where(condition)
customer.save!
end
Validation
Depending on how you're changing the Customer model, I'd include a simple vaidation on the before_update callback to see if the attribute is populated or not:
#app/models/Customer.rb
class Customer < ActiveRecord::Base
before_update :is_valid?
private
def is_valid?
return if self.attribute.present?
end
end
This will basically check if the model has the attribute populated. If it does, it means you'll then be able to update it, else it will break
--
Strong_Params
An alternative will be to set the strong_params so that the attribute you want to remain constant will not be changed when you update / create the element:
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
...
private
def strong_params
params.require(:model).permit(:only, :attributes, :to, :update)
end
end
It would be much more helpful if you explained the context as to why you need this type of functionality - that will give people the ability to create a real solution, instead of proposing ideas

Automatically setting a column to created_at during model instance creation

I have the following model (sort_timestamp is a datetime):
class Post < ActiveRecord::Base
[snip attr_accessible]
acts_as_nested_set
after_create :set_sort_timestamp
private
def set_sort_timestamp
self.sort_timestamp = self.created_at
end
end
I'm using https://github.com/collectiveidea/awesome_nested_set . This code doesn't set sort_timestamp. What am I doing wrong?
Unless I'm missing the point of what you're doing here, you're probably looking for before_create if you'd like it to save when the row is created. Otherwise you'll have to add self.save to the method, but that will cause extra database calls, so before_create might be the better option.
(Basically, the flow of what you were doing before was that the model would be created, saved to the database, and then the object would modify its attribute sort_timestamp to be created_at; this is after your database commit, and only performed in memory (so not persisted, unless you were persisting it in another way later in the code).
EDIT: Actually, this probably won't work because created_at probably won't be set before the record is created. A few options:
1) Add self.save to end of your method with after_create
2) Use Time.now if the times sort_timestamp and created_at don't have to be exactly the same.
or, 3) Try adding default value to migration: How to use created_at value as default in Rails

How do I update field with the id appended to a string on creation in rails

I have the following table and a corresponding model:
Orders
ID|ORDER_REF|....
The order ref is of the format 'ORDER#000-00'+ORDER.ID The thing is that I need to enable it such that the order ref is set on insertion. Is there a way to do this without having to do an update after the insertion, I'm using RoR here.
Do you really need that data in your database? The best way would be to just have a method on your model that returns the order ref in the desired format, based on the id in the database.
class Order < ActiveRecord::Base
def order_ref
"ORDER#000-#{self.id.to_s.rjust(3, '0')}"
end
end
With the abobe you can do this:
order = Order.create(params[:order])
order.id #=> 12
order.order_ref #=> "ORDER#000-012"
If you do need the order ref in the database, I recommend using an after_create callback:
class Order < ActiveRecord::Base
after_create :generate_order_ref
def generate_order_ref
self.order_ref = "ORDER#000-#{self.id.to_s.rjust(3, '0')}"
save
end
end
This does do an update after inserting, but I don't see any problem with that.

Rails3 timestamp mapping to legacy table

I have a legacy table with a column for the last update timestamp.
Now I do want to tell my model that the rails attribute updated_at is mapped to the legacy column.
alias_attribute :updated_at, :lastcall
Now I can access the column but it's not getting updated when i update the object.
So how can I use the rails timestamps with an legacy column?
Best,
P
Try to add this as well, which will alias the setter method.
alias_attribute :updated_at=, :lastcall=
I don't know if there's a 'proper' way of doing it, but you could do it with a before_save or before_update filter on the model.
class LegacyModel < ActiveRecord::Base
before_update :update_lastcall
private
def update_lastcall
self.lastcall = Time.now
end
end
If you don't want to get the model messy you could put it into an Observer.
I'd also like to draw your attention to this, if your timestamp column names are site-wide (as mine are). I didn't want to clutter up my models, and fortunately, you can monkey-patch ActiveRecord::Timestamp. I placed the below into a dir named lib/rails_ext/active_record.rb (I'm an organization freak) and called it with a require 'rails_ext/active_record' declaration in one of my initializers in config/initializers/.
module ActiveRecord
module Timestamp
private
def timestamp_attributes_for_update #:nodoc:
[:modified_time, :updated_at, :updated_on, :modified_at]
end
def timestamp_attributes_for_create #:nodoc:
[:created_date, :created_at, :created_on]
end
end
end
My custom attributes ended up being :modified_time and :created_date. You'd specify your :lastcall column in one of those (timestamp_attributes_for_update, I'm assuming). No mucking with your models required.

Resources