Skip to content

Commit

Permalink
Added updated_by and stamped_by for both.
Browse files Browse the repository at this point in the history
Refactored to DRY things up.
Backwards compatible with created_by.
  • Loading branch information
Reid MacDonald committed Jan 31, 2010
1 parent 35cfdb9 commit 7ea9ec7
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 93 deletions.
48 changes: 42 additions & 6 deletions README.rdoc
@@ -1,28 +1,64 @@
= CreatedBy
= UserStamps

Adds a simple +belongs_to+ association to a +creator+ model based on a magic column
+created_by+ as it's foreign key.
Adds a simple +belongs_to+ associations to a user model based on a magic columns
+created_by+ and +updated_by+ as it's foreign key.

== Example

class User < ActiveRecord::Base
end

class Item < ActiveRecord::Base
stamped_by :user
end

i = Item.find(1)
i.created_by #=> 3
i.creator #=> #<User id:3>
i.updated_by #=> 5
i.updator #=> #<User id:5>

== Configuration

You can specify configure how your +creator+ and +updator+ can be specified but
you have to pass options to +created_by+ and +updated_by+ separately:

class Item < ActiveRecord::Base
created_by :user
updated_by :admin
end

i = Item.find(1)
i.created_by #=> 3
i.creator #=> #<User id:3>
i.updated_by #=> 1
i.updator #=> #<Admin id:1>

== Options

By default, the creator is required, you can turn this off with a :required option:

created_by :user, :required => false

You could pass a :foriegn_key option to set a specific +created_by+ foriegn key but
you might as well just setup the association yourself at that point.
By default, the updator is _not_ required because on create you will need to specify
an updator, but you can turn this on with a :required option:

updated_by :admin, :required => true

You could pass a :foriegn_key option to set a specific +created_by+ or +updated_by+
foriegn key but you might as well just setup the association yourself at that point.

== Using only Created By

If you only care about creators you can just specify a +created_by+ instead of
+stamped_by+ as an option.

class Item < ActiveRecord::Base
created_by :user
end

i = Item.find(1)
i.created_by #=> 3
i.creator #=> #<User id:3>

Copyright (c) 2009 Reid MacDonald <reid@laruby.com>, released under the MIT license
Copyright (c) 2010 Reid MacDonald <reid@laruby.com>, released under the MIT license
27 changes: 0 additions & 27 deletions lib/active_record/associations/created_by.rb

This file was deleted.

50 changes: 50 additions & 0 deletions lib/active_record/associations/user_stamps.rb
@@ -0,0 +1,50 @@
module ActiveRecord # :nodoc:
module Associations # :nodoc:
module UserStamps # :nodoc:
##
# Extends ActiveRecord::Base with ClassMethods
def self.included base
base.extend(ClassMethods)
end

##
# Methods added to ActiveRecord::Base
module ClassMethods
##
# Creates associations for +created_by+ and +updated_by+ using the respective configuration of each.
def stamped_by user_model
created_by user_model
updated_by user_model
end

##
# Creates an association on the +created_by+ column to the supplied +user_model+ argument.
# You can override the +created_by+ column name with :foriegn_key but at that point you might
# as well set up the relationship yourself.
#
# Required by default.
def created_by user_model, options = {}
user_association :creator, user_model, options.reverse_merge(:foreign_key => 'created_by', :required => true)
end

##
# Creates an association on the +updated_by+ column to the supplied +user_model+ argument.
# You can override the +updated_by+ column name with :foriegn_key but at that point you might
# as well set up the relationship yourself.
#
# Not required by default.
def updated_by user_model, options = {}
user_association :updator, user_model, options.reverse_merge(:foreign_key => 'updated_by', :required => false)
end

private
def user_association association, user_model, options
configuration = {:class_name => user_model.to_s.classify}.merge!(options)

belongs_to association, :foreign_key => configuration[:foreign_key], :class_name => configuration[:class_name]
validates_presence_of association if configuration[:required]
end
end
end
end
end
60 changes: 0 additions & 60 deletions spec/created_by_spec.rb

This file was deleted.

101 changes: 101 additions & 0 deletions spec/user_stamps_spec.rb
@@ -0,0 +1,101 @@
require File.dirname(__FILE__) + '/spec_helper'

class MockActiveRecord < ActiveRecord::Base
def self.column_names
%w[id created_by updated_by]
end

def self.columns
column_names.map do |name|
ActiveRecord::ConnectionAdapters::Column.new(name.to_s, nil)
end
end
end

describe ActiveRecord::Base do
it "includes ActiveRecord::Assocations::UserStamps::ClassMethods" do
ActiveRecord::Base.methods.should be_member('stamped_by')
ActiveRecord::Base.methods.should be_member('created_by')
ActiveRecord::Base.methods.should be_member('updated_by')
end
end

describe :created_by do
describe :belongs_to do
it "creates the belongs_to :creator association with default options" do
MockActiveRecord.should_receive(:belongs_to).with(:creator, :class_name => 'MockCreator', :foreign_key => 'created_by')
MockActiveRecord.created_by :mock_creator
end

it "creates the belongs_to association with alternate foreign_key" do
MockActiveRecord.should_receive(:belongs_to).with(:creator, :class_name => 'MockCreator', :foreign_key => 'creator_id')
MockActiveRecord.created_by :mock_creator, :foreign_key => 'creator_id'
end

it "creates the belongs_to association with alternate class_name (but why?)" do
MockActiveRecord.should_receive(:belongs_to).with(:creator, :class_name => 'AnotherClassName', :foreign_key => 'created_by')
MockActiveRecord.created_by :mock_creator, :class_name => 'AnotherClassName'
end

it 'ignores all other options' do
MockActiveRecord.should_receive(:belongs_to).with(:creator, :class_name => 'MockCreator', :foreign_key => 'created_by')
MockActiveRecord.created_by :mock_creator, :foo => 'Bar'
end
end

describe :validates_presence_of do
it 'is added by default' do
MockActiveRecord.should_receive(:validates_presence_of).with(:creator)
MockActiveRecord.created_by :mock_creator
end

it 'turns off validation for creator' do
MockActiveRecord.should_not_receive(:validates_presence_of)
MockActiveRecord.created_by :mock_creator, :required => false
end
end
end

describe :updated_by do
describe :belongs_to do
it "creates the belongs_to :updator association with default options" do
MockActiveRecord.should_receive(:belongs_to).with(:updator, :class_name => 'MockUpdator', :foreign_key => 'updated_by')
MockActiveRecord.updated_by :mock_updator
end

it "creates the belongs_to association with alternate foreign_key" do
MockActiveRecord.should_receive(:belongs_to).with(:updator, :class_name => 'MockUpdator', :foreign_key => 'updator_id')
MockActiveRecord.updated_by :mock_updator, :foreign_key => 'updator_id'
end

it "creates the belongs_to association with alternate class_name (but why?)" do
MockActiveRecord.should_receive(:belongs_to).with(:updator, :class_name => 'AnotherClassName', :foreign_key => 'updated_by')
MockActiveRecord.updated_by :mock_updator, :class_name => 'AnotherClassName'
end

it 'ignores all other options' do
MockActiveRecord.should_receive(:belongs_to).with(:updator, :class_name => 'MockUpdator', :foreign_key => 'updated_by')
MockActiveRecord.updated_by :mock_updator, :foo => 'Bar'
end
end

describe :validates_presence_of do
it 'is not added by default' do
MockActiveRecord.should_not_receive(:validates_presence_of).with(:updator)
MockActiveRecord.updated_by :mock_updator
end

it 'turns on validation for updator' do
MockActiveRecord.should_receive(:validates_presence_of)
MockActiveRecord.updated_by :mock_updator, :required => true
end
end
end

describe :stamped_by do
it 'uses the same model on created_by and updated_by' do
MockActiveRecord.should_receive(:created_by).with(:user)
MockActiveRecord.should_receive(:updated_by).with(:user)
MockActiveRecord.stamped_by :user
end
end

0 comments on commit 7ea9ec7

Please sign in to comment.