OSDN Git Service

initialize repository master
authorokimoto <okimoto@good-day.co.jp>
Wed, 9 Dec 2009 05:58:16 +0000 (14:58 +0900)
committerokimoto <okimoto@good-day.co.jp>
Wed, 9 Dec 2009 05:58:16 +0000 (14:58 +0900)
.gitignore [new file with mode: 0644]
README [new file with mode: 0644]
README.ja [new file with mode: 0644]
Rakefile [new file with mode: 0644]
lib/context_monitor.rb [new file with mode: 0644]
rails/init.rb [new file with mode: 0644]
spec/context_monitor_spec.rb [new file with mode: 0644]
spec/db/database.yml [new file with mode: 0644]
spec/db/schema.rb [new file with mode: 0644]
spec/spec_helper.rb [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..3b53699
--- /dev/null
@@ -0,0 +1,4 @@
+*~
+*.log
+*.db
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..364d619
--- /dev/null
+++ b/README
@@ -0,0 +1,37 @@
+ContextMonitor
+===========
+
+This plugin provides the capability for monitoring operations (create/update).
+
+Example
+=======
+
+  ActiveRecord::Schema.define(:version => 0) do
+    create_table :articles do |t|
+      t.string  :name
+      t.integer :created_by_id
+      t.integer :updated_by_id
+      t.timestamps
+    end
+  end
+
+  class Article < ActiveRecord::Base
+    context_monitor :user, :suffix => 'by'
+  end
+
+  User.current = User.find(10)
+  article = Article.create!(:name => 'example')
+  p article.created_by.id # 10
+  p article.updated_by.id # 10
+  p article.created_by == User.current # true
+  p article.updated_by == User.current # true
+
+  User.current = User.find(20)
+  article.name = 'test'
+  p article.created_by.id # 10
+  p article.updated_by.id # 20
+  p article.created_by == User.current # false
+  p article.updated_by == User.current # true
+
+
+Copyright (c) 2008 Good-Day, Inc
diff --git a/README.ja b/README.ja
new file mode 100644 (file)
index 0000000..1d436a0
--- /dev/null
+++ b/README.ja
@@ -0,0 +1,37 @@
+ContextMonitor
+===========
+
+このプラグインはレコードに対する create/update を記録する機能を提供します。
+
+Example
+=======
+
+  ActiveRecord::Schema.define(:version => 0) do
+    create_table :articles do |t|
+      t.string  :name
+      t.integer :created_by_id
+      t.integer :updated_by_id
+      t.timestamps
+    end
+  end
+
+  class Article < ActiveRecord::Base
+    context_monitor :user, :suffix => 'by'
+  end
+
+  User.current = User.find(10)
+  article = Article.create!(:name => 'example')
+  p article.created_by.id # 10
+  p article.updated_by.id # 10
+  p article.created_by == User.current # true
+  p article.updated_by == User.current # true
+
+  User.current = User.find(20)
+  article.name = 'test'
+  p article.created_by.id # 10
+  p article.updated_by.id # 20
+  p article.created_by == User.current # false
+  p article.updated_by == User.current # true
+
+
+Copyright (c) 2008 Good-Day, Inc
diff --git a/Rakefile b/Rakefile
new file mode 100644 (file)
index 0000000..e1724b5
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,48 @@
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+require 'rake/gempackagetask'
+require 'spec/rake/spectask'
+
+desc 'Default: run unit tests.'
+task :default => :spec
+
+Spec::Rake::SpecTask.new do |t|
+  t.spec_files = FileList['spec/*_spec.rb']
+  t.spec_opts = ['-c']
+end
+
+PKG_FILES = FileList[
+  'lib/monitor.rb',
+  'rails/init.rb',
+  'tasks/monitor_tasks.rake',
+  'spec/*'
+]
+spec = Gem::Specification.new do |s|
+  s.name = 'monitor'
+  s.version = '0.0.1'
+  s.author = 'Good-Day, Inc.'
+  s.email = 'info@good-day.co.jp'
+  s.homepage = 'http://www.good-day.jp/'
+  s.platform = Gem::Platform::RUBY
+  s.summary = 'Add functionallity of monitoring operations'
+  s.files = PKG_FILES.to_a
+  s.require_path = 'lib'
+  s.has_rdoc = false
+  s.extra_rdoc_files = ['README']
+end
+
+desc 'Turn this plugin into a gem.'
+Rake::GemPackageTask.new(spec) do |pkg|
+  pkg.gem_spec = spec
+end
+
+desc 'Generate documentation for the context_monitor plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+  rdoc.rdoc_dir = 'rdoc'
+  rdoc.title    = 'Monitor'
+  rdoc.options << '--line-numbers' << '--inline-source'
+  rdoc.rdoc_files.include('README')
+  rdoc.rdoc_files.include('lib/**/*.rb')
+end
+
diff --git a/lib/context_monitor.rb b/lib/context_monitor.rb
new file mode 100644 (file)
index 0000000..edcfc31
--- /dev/null
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+module ActiveRecord
+
+  # 作成時および更新時の情報を記録するモジュール。
+  module ContextMonitor
+
+    def self.included(base) # :ndoc:
+      base.extend(ClassMethods)
+    end
+
+    module ClassMethods
+      #
+      # モデルクラスで呼び出すことにより指定した対象の状況を記録するようになる。
+      #
+      # <tt>monitor</tt> : 記録する対象のモデルクラスの名前を指定する。
+      #                    デフォルトは <tt>User</tt>
+      #                    ここで使用するモデルクラスには current_id というクラスメソッドが
+      #           定義されている必要がある。
+      #
+      def context_monitor(target_model_name = :user, options = {})
+        model_name  = target_model_name.to_s.capitalize
+        suffix = (options[:suffix] || 'by').to_s
+        created = 'created_' + suffix
+        updated = 'updated_' + suffix
+
+        class_eval do
+          callbacks = Callbacks.new(model_name, created, updated)
+          before_create callbacks
+          before_update callbacks
+          belongs_to created, :class_name => model_name, :foreign_key => "#{created}_id"
+          belongs_to updated, :class_name => model_name, :foreign_key => "#{updated}_id"
+        end
+      end
+      alias monitor context_monitor
+    end
+
+    class Callbacks # :ndoc: all
+      def initialize(model_name, created, updated)
+        @model = model_name.constantize
+        @created_id = "#{created}_id"
+        @updated_id = "#{updated}_id"
+      end
+      def before_create(record)
+        record.write_attribute(@created_id, @model.current_id) if include_column_and_nil?(record, @created_id)
+        before_update record
+      end
+      def before_update(record)
+        record.write_attribute(@updated_id, @model.current_id) if include_column?(record, @updated_id)
+      end
+
+      private
+      def column_names(record)
+        @column_names ||= record.class.column_names
+      end
+
+      def include_column?(record, column)
+        column_names(record).include? column
+      end
+
+      def include_column_and_nil?(record, column)
+        include_column?(record, column) && record.read_attribute(column).nil?
+      end
+    end
+  end
+end
+
+ActiveRecord::Base.__send__(:include, ActiveRecord::ContextMonitor)
+
diff --git a/rails/init.rb b/rails/init.rb
new file mode 100644 (file)
index 0000000..2038d66
--- /dev/null
@@ -0,0 +1 @@
+require "context_monitor"
diff --git a/spec/context_monitor_spec.rb b/spec/context_monitor_spec.rb
new file mode 100644 (file)
index 0000000..3a272be
--- /dev/null
@@ -0,0 +1,197 @@
+# -*- coding: utf-8 -*-
+
+require File.join(File.dirname(__FILE__), 'spec_helper')
+
+module CurrentMethods
+  def current=(user)
+    @current = user
+  end
+  def current
+    @current
+  end
+  def current_id
+    current.id if current
+  end
+end
+
+describe ActiveRecord::ContextMonitor do
+  module ActiveRecord::ContextMonitor
+    describe ClassMethods do
+      it 'defines context_monitor' do
+        ClassMethods.instance_methods.should be_include('context_monitor')
+      end
+
+      it 'defines alias monitor' do
+        ClassMethods.instance_methods.should be_include('monitor')
+      end
+    end
+
+    describe Callbacks do
+      before do
+        @created_by = 'created_by'
+        @updated_by = 'updated_by'
+        @callbacks = Callbacks.new('Object', @created_by, @updated_by)
+
+        @a, @b, @c = 'a', 'b', 'c'
+        @record = mock('record', :null_object => true)
+        @record.stub(:class => @record)
+      end
+
+      it 'sets model and needed ids' do
+        created_by, updated_by = @created_by, @updated_by
+        @callbacks.instance_eval do
+          @model.should == Object
+          @created_id.should == "#{created_by}_id"
+          @updated_id.should == "#{updated_by}_id"
+        end
+      end
+
+      it 'can list all column names' do
+        @record.should_receive(:column_names).and_return([@a, @b, @c])
+        column_names = @callbacks.__send__(:column_names, @record)
+        column_names.should == [@a, @b, @c]
+      end
+
+      it 'can test does record has column' do
+        @record.stub(:column_names).and_return([@a, @b, @c])
+        [@a, @b, @c].each do |target|
+          @callbacks.__send__(:include_column?, @record, target).should be_true
+        end
+        [nil, :hoge, 'test', 1].each do |target|
+          @callbacks.__send__(:include_column?, @record, target).should be_false
+        end
+      end
+
+      it 'can test does record has column and it value is nil' do
+        column = :test
+        @callbacks.should_receive(:include_column?).
+          with(@record, column).and_return(true)
+        @record.should_receive(:read_attribute).with(column).and_return(nil)
+        @callbacks.__send__(:include_column_and_nil?, @record, column).should be_true
+      end
+    end
+  end
+
+  describe 'Default user' do
+    before(:all) do
+      class User < ActiveRecord::Base
+        extend CurrentMethods
+      end
+
+      class Article < ActiveRecord::Base
+        context_monitor :user
+      end
+    end
+
+    describe 'create' do
+      before do
+        @user = User.create(:name => 'test')
+        User.current = @user
+        @article = Article.create!(:title => 'title',
+                                   :body  => 'body' * 100)
+      end
+      after do
+        @user = nil
+        User.current = nil
+      end
+      it 'has same id' do
+        @article.created_by_id.should == @user.id
+        @article.updated_by_id.should == @user.id
+      end
+      it 'has same object' do
+        @article.created_by.should == @user
+        @article.updated_by.should == @user
+      end
+    end
+
+    describe 'update' do
+      before do
+        @user1 = ::User.create(:name => 'alice')
+        User.current = @user1
+        @article = ::Article.create!(:title => 'title',
+                                     :body  => 'body' * 100)
+        @article.title = 'change title'
+        @user2 = ::User.create(:name => 'bob')
+        User.current = @user2
+        @article.save!
+      end
+      after do
+        @user1 = nil
+        @user2 = nil
+        User.current = nil
+      end
+      it 'has collect id' do
+        @article.created_by_id.should == @user1.id
+        @article.updated_by_id.should == @user2.id
+      end
+      it 'has collect object' do
+        @article.created_by.should == @user1
+        @article.updated_by.should == @user2
+      end
+    end
+
+    describe "no current user" do
+      before do
+        @user = User.create(:name => 'good')
+        @article = Article.create!(:title => 'title',
+                                   :body  => 'body' * 100)
+      end
+      after do
+        @user = nil
+        User.current = nil
+      end
+      it 'all is nil' do
+        @article.created_by_id.should be_nil
+        @article.updated_by_id.should be_nil
+        @article.created_by.should be_nil
+        @article.updated_by.should be_nil
+      end
+    end
+  end
+
+  describe 'Set Class and Suffix' do
+    before(:all) do
+      class Country < ActiveRecord::Base
+        extend CurrentMethods
+      end
+
+      class Car < ActiveRecord::Base
+        context_monitor :country, :suffix => :in
+      end
+    end
+
+    before do
+      @country1 = Country.create(:name => 'Japan')
+      Country.current = @country1
+      @car = Car.create!(:name => 'XXX', :price => 100)
+      @country2 = Country.create(:name => 'America')
+    end
+
+    after do
+      @country1 = nil
+      Country.current = nil
+    end
+
+    it 'has same id' do
+      @car.created_in_id.should == @country1.id
+      @car.updated_in_id.should == @country1.id
+    end
+
+    it 'has same object' do
+      @car.created_in.should == @country1
+      @car.updated_in.should == @country1
+    end
+
+    it 'has collect id and object when update' do
+      Country.current = @country2
+      @car.price = 200
+      @car.save!
+
+      @car.created_in_id.should == @country1.id
+      @car.updated_in_id.should == @country2.id
+      @car.created_in.should == @country1
+      @car.updated_in.should == @country2
+    end
+  end
+end
+
diff --git a/spec/db/database.yml b/spec/db/database.yml
new file mode 100644 (file)
index 0000000..e7da6f8
--- /dev/null
@@ -0,0 +1,19 @@
+
+sqlite3:
+  :adapter: sqlite3
+  :database: spec/db/tiny_monitor.sqlite3.db
+
+# postgresql:
+#   :adapter: postgresql
+#   :username: rails
+#   :password: rails
+#   :database: display_monitor_test
+#   :min_messages: ERROR
+# 
+# mysql:
+#   :adapter: mysql
+#   :host: localhost
+#   :username: rails
+#   :password: rails
+#   :database: display_monitor_test
+
diff --git a/spec/db/schema.rb b/spec/db/schema.rb
new file mode 100644 (file)
index 0000000..baac4b8
--- /dev/null
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+
+ActiveRecord::Schema.define(:version => 0) do
+  create_table :users, :force => true do |t|
+    t.string  :name
+  end
+  create_table :articles, :force => true do |t|
+    t.string  :title
+    t.text    :body
+    t.integer :created_by_id
+    t.integer :updated_by_id
+  end
+  create_table :countries, :force => true do |t|
+    t.string :name
+  end
+  create_table :cars, :force => true do |t|
+    t.string  :name
+    t.integer :price
+    t.integer :created_in_id
+    t.integer :updated_in_id
+  end
+end
+
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
new file mode 100644 (file)
index 0000000..36ba9d2
--- /dev/null
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+
+plugin_spec_dir = File.dirname(__FILE__)
+
+$:.unshift File.join(plugin_spec_dir, '..', 'lib')
+
+require 'active_record'
+require 'rubygems'
+require 'spec'
+
+ActiveRecord::Base.logger = Logger.new(plugin_spec_dir + "/debug.log")
+
+database = YAML.load(File.read(plugin_spec_dir + '/db/database.yml'))
+ActiveRecord::Base.establish_connection(database[ENV['DB'] || 'sqlite3'])
+load File.join(plugin_spec_dir, 'db', 'schema.rb')
+
+require File.join(plugin_spec_dir, '..', 'rails', 'init')
+