I have a couple of fields created_by and updated_by in most of my tables. This would contain the user id of the user who created or updated the Object. is it possible to have a similar function like how rails handles created_at? I basically want it to function the same way as the timestamps insertion. I should be able to define in the columns in the migration script and configure rails to fetch the user object from a helper method everytime when it changes the particular object. Is there a direct way to do it or is there a plugin which does this?
Also you can do this without gems but with Rails Observers
You can create observer like this:
class UserTouchObserver < ActiveRecord::Observer
observe :product, :post, :comment
def after_create(model)
update_attribute(:created_by, current_user.id) if model.respond_to?(:created_by)
end
def after_update(model)
update_attribute(:updated_by, current_user.id) if model.respond_to?(:updated_by)
end
end
I was able to find a few plugins on github that do just this:
https://github.com/jnunemaker/user_stamp
https://github.com/bokmann/userstamp_basic
Related
I want that when new entry insert in Logs model column of another model updated after that.
For this I want to write trigger on insertion but I cant find any helping material how to write trigger in rails.
Did you try Callbacks? It seems like this is what you are after.
Maybe something like this:
class Post < ActiveRecord::Base
after_save :add_log_entry
private
def add_log_entry
LogModel.create(message: 'post thingy')
end
end
I want to preview what the model will look like when saved without currently saving to the database.
I am using #event.attributes = because that assigns but does not save attributes for #event to the database.
However, when I also try to assign the audiences association, Rails inserts new records into the audiences_events join table. Not cool. Is there a way to preview what these new associations will look like without inserting into the join table?
Model
class Event < ActiveRecord::Base
has_and_belongs_to_many :audiences # And vice versa for the Audience model.
end
Controller
class EventsController < ApplicationController
def preview
#event = Event.find(params[:id])
#event.attributes = event_params
end
private
def event_params
params[:event].permit(:name, :start_time, :audiences => [:id, :name]
end
end
Possible Solutions?
Possible solutions that I thought of, but don't know how to do:
Using some sort of method that assigns associations, but does not persist them.
disabling all database writes for this one action (I dont know how to do that).
Rolling back all database changes at the end of this action
Any help with these would be great!
UPDATE:
After the reading the great answers below, I ended up writing this service class that assigns the non-nested attributes to the Event model, then calls collection.build on each of the nested params. I made a little gist. Happy to receive comments/suggestions.
https://gist.github.com/jameskerr/69cedb2f30c95342f64a
In these docs you have:
When are Objects Saved?
When you assign an object to a has_and_belongs_to_many association, that object is automatically saved (in order to update the join table). If you assign multiple objects in one statement, then they are all saved.
If you want to assign an object to a has_and_belongs_to_many association without saving the object, use the collection.build method.
Here is a good answer for Rails 3 that goes over some of the same issues
Rails 3 has_and_belongs_to_many association: how to assign related objects without saving them to the database
Transactions
Creating transactions is pretty straight forward:
Event.transaction do
#event.audiences.create!
#event.audiences.first.destroy!
end
Or
#event.transaction do
#event.audiences.create!
#event.audiences.first.destroy!
end
Notice the use of the "bang" methods create! and destroy!, unlike create which returns false create! will raise an exception if it fails and cause the transaction to rollback.
You can also manually trigger a rollback anywhere in the a transaction by raising ActiveRecord::Rollback.
Build
build instantiates a new related object without saving.
event = Event.new(name: 'Party').audiences.build(name: 'Party People')
event.save # saves both event and audiences
I know that this is a pretty old question, but I found a solution that works perfectly for me and hope it could save time to someone else:
class A
has_many :bs, class_name 'B'
end
class B
belongs_to :a, class_name: 'A'
end
a.bs.target.clear
new_bs.each {|new_b| a.bs.build new_b.attributes.except('created_at', 'updated_at', 'id') }
you will avoid autosave that Rails does when you do a.bs = new_bs
I have Audited (formerly acts_as_audited) setup and working. The user_id is successfully saved in the audit table but I can't figure out an efficient way to save the tenant_id (I have multitenancy setup with scopes). I have tried using the Associated Audits technique described in the README but that doesn't work for me.
My current solution is to use the after_audit callback in every model (can be implemented with Rails concerns) to get the last audit and save the tenant_id:
def after_audit
audit = Audit.last
audit.tenant_id = self.tenant_id
audit.save!
end
Whilst this works it seems like it would be inefficient to have to query for the audit again and then update it. It would make more sense to me to add the tenant_id to the audit before it saves but I can't figure out how to do this. Is it possible to add the tenant_id to the audit before saving? If yes, then how?
EDIT:
I've also tried including my default tenant scope in my Audit model but it does not seem to be called:
audit.rb
class Audit < ActiveRecord::Base
default_scope { where(tenant_id: Tenant.current_id) }
application_controller.rb
class ApplicationController < ActionController::Base
around_action :scope_current_tenant
def scope_current_tenant
Tenant.current_id = current_tenant.id
yield
ensure
Tenant.current_id = nil
end
EDIT: 2/1/16
I still haven't implemented a solution to this however my current thoughts would be to use:
#model_name.rb
def after_audit
audit = self.audits.last
audit.business_id = self.business_id
audit.save!
end
In this code we get the last audit for the current model. This way we are only dealing with the current model, there is no chance of adding the audit to another business (as far as I can tell). I would add this code into a concern to keep it DRY.
I still can't get normal Rails callbacks to work within the Audit model. The only other way I see at the moment is to fork and modified the gem source code.
I was tasked with implementing Auditing, and also adding a reference to an Org. The migration adds this line:
t.references :org, type: :uuid, index: true, null: true
To save an org_id, I ended up writing an initializer - audited.rb. That file looks like this:
Rails.configuration.after_initialize do
Audited.audit_class.class_eval do
belongs_to :org, optional: true
default_scope MyAppContext.context_scope
before_create :ensure_org
private
def ensure_org
return unless auditable.respond_to? :org_id
self.org_id = auditable.org_id
end
end
end
Hope this helps!
I have recently added Acts As Tenant gem to a Rails app that is running the Audited gem. I was running into the same problem. I had added
acts_as_tenant :account
to the Audit model but it didn't do anything. I learned that you can't override in the Audit model but have to create a custom audit model that inherits from it. So I created the model:
custom_audit.rb
class CustomAudit < Audited::Audit
acts_as_tenant :account
end
I then added the initializer file audited.rb in confi/initializers like so:
Audited.config do |config|
config.audit_class = CustomAudit
end
I was still having the problem where all my multitenancy was working except the show_audit view. I finally deleted all of my audits from both tenants in my test setup. It worked! I can now add new audits and they scope just fine. But I still need to merge the actual client DBs into one, and I don't want to lose the history in the audit table... Not sure how to fix that.
So when I try to access the Audits it fails with current_tenant being nil. Not sure why deleting all of the current records in the table fixes it, but I need to find a way around it.
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
I'm trying to override the way rails apply and id to an associated object, for example:
There are 2 simple models:
class Album < ActiveRecord::Base
has_many :photos
end
class Photo < ActiveRecord::Base
belongs_to :album
end
And then I want to do this:
album = Album.new :title => 'First Album'
album.photos.build
album.save #=> true
On this case I've created a plugin that overrides the id property and replaces it to a hashed string, so what I want to do is find the methods where this album_id is being replaced for my custom method instead of the int and be able to converted before it's saved.
But I want to act globally inside Rails structure because since it will be a sort of plugin I want to make this action work on dynamic models, that's why I can't create an before_save validation on the model.
I'm not sure if it's easy to understand, but I hope someone could help me on that..
Here's a screenshot of my current table so you can see what is happening:
SQLite3 DB http://cl.ly/1j3U/content
So as you can see the album_id it's being replaced for my custom ruby object when its saved...I've disabled the plugin and then it saved normally with records 11 and 12...
I want just act on a rails action and converted with my custom methods, something like
def rails_association_replaced_method(record)
#take the record associations and apply a to_i custom method before save
super(record)
end
something like this :)
Well I hope this didn't get too complicated
Cheers
It seems if I only override theActiveRecord::Base save method do the job if handled properly
define_method 'save' do
int_fields = self.class.columns.find_all { |column| column.type == :integer }
int_fields.each do |field|
if self.attributes[field.name]
self.attributes[field.name] = self.attributes[field.name].to_i
end
end
super
end
And this shall replace all the integer fields from the Current Model applying a to_i method over the result.
Rails is unfriendly to that kind of change to the defaults. What's your end goal here?