after_timestamps 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,58 @@
1
+ # after_timestamps
2
+
3
+ ## Summary
4
+
5
+ Plugin for Ruby on Rails that gives you a way to add a callback to the ActiveRecord callback chain that will be executed right after the record's timestamp columns are set, but before the record is actually saved to the database. This is useful if you want to do something with the timestamps, such as defaulting another time column to created_at, or rolling back a timestamp by a certain amount.
6
+
7
+ ## Example
8
+
9
+ Let's say you have a migration like this:
10
+
11
+ class AddFoo < ActiveRecord::Migration
12
+ def self.up
13
+ create_table :foo {|t| t.timestamps }
14
+ end
15
+ def self.down
16
+ drop_table :foo
17
+ end
18
+ end
19
+
20
+ And a model like this:
21
+
22
+ class Foo < ActiveRecord::Base
23
+ after_timestamps_on_create :roll_back_created_at_by_two_hours
24
+ after_timestamps_on_update :roll_back_updated_at_by_two_hours
25
+
26
+ private
27
+ def roll_back_created_at_by_two_hours
28
+ self.created_at -= 2.hours
29
+ end
30
+
31
+ def roll_back_updated_at_by_two_hours
32
+ self.updated_at -= 2.hours
33
+ end
34
+ end
35
+
36
+ Now if you say
37
+
38
+ Foo.create!
39
+
40
+ `created_at` and `updated_at` will be set, but 2 hours will be subtracted from them before they're put into the database.
41
+
42
+ ## Installation
43
+
44
+ 1. Run `gem install after_timestamps` (probably as root)
45
+ 2. Add `config.gem 'after_timestamps'` to environment.rb
46
+ 3. Optionally run `rake gems:build`
47
+
48
+ ## Support
49
+
50
+ If you find any bugs with this plugin, feel free to:
51
+
52
+ * file a bug report in the [Issues area on Github](http://github.com/mcmire/after_timestamps/issues)
53
+ * fork the [project on Github](http://github.com/mcmire/after_timestamps) and send me a pull request
54
+ * email me (*firstname* dot *lastname* at gmail dot com)
55
+
56
+ ## Author/License
57
+
58
+ (c) 2008-2010 Elliot Winkler. Released under the MIT license.
@@ -0,0 +1,60 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ require File.dirname(__FILE__) + "/lib/mcmire/after_timestamps/version.rb"
5
+
6
+ begin
7
+ require 'jeweler'
8
+ Jeweler::Tasks.new do |gem|
9
+ gem.version = Mcmire::AfterTimestamps::VERSION
10
+ gem.name = "after_timestamps"
11
+ gem.summary = %Q{Rails plugin that provides an AR callback right after timestamps are set and before the record is saved}
12
+ gem.description = %Q{Plugin for Ruby on Rails that gives you a way to add a callback to the ActiveRecord callback chain that will be executed right after the record's timestamp columns are set, but before the record is actually saved to the database. This is useful if you want to do something with the timestamps, such as defaulting another time column to created_at, or rolling back a timestamp by a certain amount.}
13
+ gem.authors = ["Elliot Winkler"]
14
+ gem.email = "elliot.winkler@gmail.com"
15
+ gem.homepage = "http://github.com/mcmire/after_timestamps"
16
+ gem.add_dependency "activerecord", ">= 1.2.6"
17
+ gem.add_development_dependency "mcmire-context", ">= 0.5.6"
18
+ gem.add_development_dependency "mcmire-matchy", ">= 0.4.1"
19
+ gem.add_development_dependency "rr", ">= 0.10.5"
20
+ gem.add_development_dependency "rr-matchy", ">= 0.1.0"
21
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
22
+ end
23
+ Jeweler::GemcutterTasks.new
24
+ rescue LoadError
25
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
26
+ end
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/*_test.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+ begin
36
+ require 'rcov/rcovtask'
37
+ Rcov::RcovTask.new do |test|
38
+ test.libs << 'test'
39
+ test.pattern = 'test/**/*_test.rb'
40
+ test.verbose = true
41
+ end
42
+ rescue LoadError
43
+ task :rcov do
44
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
45
+ end
46
+ end
47
+
48
+ task :test => :check_dependencies
49
+
50
+ task :default => :test
51
+
52
+ require 'rake/rdoctask'
53
+ Rake::RDocTask.new do |rdoc|
54
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
55
+
56
+ rdoc.rdoc_dir = 'rdoc'
57
+ rdoc.title = "after_timestamps #{version}"
58
+ rdoc.rdoc_files.include('README*')
59
+ rdoc.rdoc_files.include('lib/**/*.rb')
60
+ end
@@ -0,0 +1,63 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{after_timestamps}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Elliot Winkler"]
12
+ s.date = %q{2010-01-07}
13
+ s.description = %q{Plugin for Ruby on Rails that gives you a way to add a callback to the ActiveRecord callback chain that will be executed right after the record's timestamp columns are set, but before the record is actually saved to the database. This is useful if you want to do something with the timestamps, such as defaulting another time column to created_at, or rolling back a timestamp by a certain amount.}
14
+ s.email = %q{elliot.winkler@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "README.md"
17
+ ]
18
+ s.files = [
19
+ "README.md",
20
+ "Rakefile",
21
+ "after_timestamps.gemspec",
22
+ "lib/mcmire/after_timestamps.rb",
23
+ "lib/mcmire/after_timestamps/version.rb",
24
+ "rails/init.rb",
25
+ "test/after_timestamps_test.rb",
26
+ "test/helper.rb"
27
+ ]
28
+ s.homepage = %q{http://github.com/mcmire/after_timestamps}
29
+ s.rdoc_options = ["--charset=UTF-8"]
30
+ s.require_paths = ["lib"]
31
+ s.rubygems_version = %q{1.3.5}
32
+ s.summary = %q{Rails plugin that provides an AR callback right after timestamps are set and before the record is saved}
33
+ s.test_files = [
34
+ "test/after_timestamps_test.rb",
35
+ "test/helper.rb"
36
+ ]
37
+
38
+ if s.respond_to? :specification_version then
39
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
40
+ s.specification_version = 3
41
+
42
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
43
+ s.add_runtime_dependency(%q<activerecord>, [">= 1.2.6"])
44
+ s.add_development_dependency(%q<mcmire-context>, [">= 0.5.6"])
45
+ s.add_development_dependency(%q<mcmire-matchy>, [">= 0.4.1"])
46
+ s.add_development_dependency(%q<rr>, [">= 0.10.5"])
47
+ s.add_development_dependency(%q<rr-matchy>, [">= 0.1.0"])
48
+ else
49
+ s.add_dependency(%q<activerecord>, [">= 1.2.6"])
50
+ s.add_dependency(%q<mcmire-context>, [">= 0.5.6"])
51
+ s.add_dependency(%q<mcmire-matchy>, [">= 0.4.1"])
52
+ s.add_dependency(%q<rr>, [">= 0.10.5"])
53
+ s.add_dependency(%q<rr-matchy>, [">= 0.1.0"])
54
+ end
55
+ else
56
+ s.add_dependency(%q<activerecord>, [">= 1.2.6"])
57
+ s.add_dependency(%q<mcmire-context>, [">= 0.5.6"])
58
+ s.add_dependency(%q<mcmire-matchy>, [">= 0.4.1"])
59
+ s.add_dependency(%q<rr>, [">= 0.10.5"])
60
+ s.add_dependency(%q<rr-matchy>, [">= 0.1.0"])
61
+ end
62
+ end
63
+
@@ -0,0 +1,99 @@
1
+ module Mcmire
2
+ # You probably know that every time a record is created, its created_at field
3
+ # is automatically filled in. And every time a record is updated, its updated_at
4
+ # field is filled in. This "magic" happens because of ActiveRecord's
5
+ # Timestamp module, which is mixed in when ActiveRecord::Base is require()'d.
6
+ #
7
+ # Unfortunately, if you want to adjust these times for any reason before the
8
+ # record is actually saved to the database, there isn't a built-in way to do
9
+ # this. So, what we have to end up doing is inserting another method into the
10
+ # ActiveRecord callback chain. However, doing so can be kind of tricky if you
11
+ # don't know what's going on behind the scenes. This is because ActiveRecord
12
+ # starts out with a set of standard methods in AR::Base and then several modules
13
+ # are mixed in that override some of those methods.
14
+ #
15
+ # The methods that we are concerned about are the ones that are called when
16
+ # you say `some_record.save`:
17
+ #
18
+ # save
19
+ # |
20
+ # create_or_update
21
+ # / \
22
+ # create update
23
+ #
24
+ # The modules which are mixed into AR::Base that we are concerned about are
25
+ # AR::Timestamp and AR::Callbacks. Both use alias_method_chain to override
26
+ # the methods, so you will see that below. Anyway, when AR::Timestamp is mixed
27
+ # in this is what the call tree like:
28
+ #
29
+ # save
30
+ # |
31
+ # create_or_update
32
+ # ......./ \.......
33
+ # / \
34
+ # create(_with_timestamps) update(_with_timestamps)
35
+ # | |
36
+ # create_without_timestamps update_without_timestamps
37
+ #
38
+ # (The parentheses are there to indicate the effects of alias_method_chain.)
39
+ #
40
+ # And when AR::Callbacks is mixed in, the tree changes again:
41
+ #
42
+ # save
43
+ # |
44
+ # create_or_update(_with_callbacks)
45
+ # |
46
+ # before_save
47
+ # |
48
+ # create_or_update_without_callbacks
49
+ # ............/ ^ | ^ \..........
50
+ # v | | | v
51
+ # create(_with_callbacks) | | | update(_with_callbacks)
52
+ # | | | | |
53
+ # before_create | | | before_update
54
+ # | | | | |
55
+ # create_without_callbacks | | | update_without_callbacks
56
+ # | | | | |
57
+ # create_without_timestamps / | \ update_without_timestamps
58
+ # | / | \ |
59
+ # after_create .......´ | '....... after_update
60
+ # |
61
+ # v
62
+ # after_save
63
+ #
64
+ # As you can see, when the before_save callback is fired, the timestamps
65
+ # haven't been set yet. Even at before_create/before_update, the timestamps
66
+ # still haven't been set yet. Of course, by the time after_create/after_update,
67
+ # it's too late to adjust the timestamps, since the record has already been
68
+ # saved.
69
+ #
70
+ # That's why we need to insert a custom callback in the chain. We do this by
71
+ # simply alias_method_chain()'ing create_without_timestamps and
72
+ # update_without_timestamps to do what we want.
73
+ #
74
+ module AfterTimestamps
75
+ def self.included(klass)
76
+ klass.class_eval do
77
+ alias_method_chain :create_without_timestamps, :after_timestamps
78
+ alias_method_chain :update_without_timestamps, :after_timestamps
79
+ define_callbacks :after_timestamps_on_create, :after_timestamps_on_update
80
+ end
81
+ end
82
+
83
+ def after_timestamps_on_create() end
84
+ def after_timestamps_on_update() end
85
+
86
+ private
87
+ # Override create_with_timestamps to call the after_timestamps_on_create callback
88
+ def create_without_timestamps_with_after_timestamps
89
+ return false if callback(:after_timestamps_on_create) == false
90
+ create_without_timestamps_without_after_timestamps
91
+ end
92
+
93
+ # Same thing, only for update_with_timestamps
94
+ def update_without_timestamps_with_after_timestamps
95
+ return false if callback(:after_timestamps_on_update) == false
96
+ update_without_timestamps_without_after_timestamps
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,5 @@
1
+ module Mcmire
2
+ module AfterTimestamps
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ require 'activerecord'
2
+
3
+ require 'mcmire/after_timestamps'
4
+
5
+ ActiveRecord::Base.class_eval do
6
+ include Mcmire::AfterTimestamps
7
+ end
@@ -0,0 +1,59 @@
1
+ require 'helper'
2
+
3
+ ActiveRecord::Base.establish_connection(
4
+ "adapter" => "sqlite3",
5
+ "database" => ":memory:"
6
+ )
7
+
8
+ ActiveRecord::Schema.define do
9
+ create_table :posts do |t|
10
+ t.timestamps
11
+ t.datetime :posted_at, :null => false
12
+ t.string :formatted_created_at
13
+ t.integer :timestamp
14
+ end
15
+ end
16
+
17
+ class Post < ActiveRecord::Base
18
+ after_timestamps_on_create :set_posted_at_to_created_at
19
+ after_timestamps_on_create {|post| post.formatted_created_at = post.created_at.strftime("%b/%Y-%d %D %H%M..%S") }
20
+ after_timestamps_on_update :rollback_created_at
21
+ after_timestamps_on_update {|post| post.timestamp = post.created_at.to_i }
22
+
23
+ def set_posted_at_to_created_at
24
+ self.posted_at = self.created_at
25
+ end
26
+ def rollback_created_at
27
+ self.posted_at -= 2.days
28
+ end
29
+ end
30
+
31
+ class AfterTimestampsTest < Test::Unit::TestCase
32
+ test "after_timestamps_on_create symbol is called on create" do
33
+ stub(Time).now { Time.local(2009) }
34
+ post = Post.new
35
+ post.save!
36
+ post.posted_at.should == Time.local(2009)
37
+ end
38
+
39
+ test "after_timestamps_on_create proc is called on create" do
40
+ stub(Time).now { Time.local(2009) }
41
+ post = Post.new
42
+ post.save!
43
+ post.formatted_created_at.should == "Jan/2009-01 01/01/09 0000..00"
44
+ end
45
+
46
+ test "after_timestamps_on_update symbol is called on update" do
47
+ stub(Time).now { Time.local(2009) }
48
+ post = Post.create!
49
+ post.save!
50
+ post.posted_at.should == Time.local(2008, 12, 30)
51
+ end
52
+
53
+ test "after_timestamp_on_update proc is called on update" do
54
+ stub(Time).now { Time.local(2009) }
55
+ post = Post.create!
56
+ post.save!
57
+ post.timestamp.should == 1230789600
58
+ end
59
+ end
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+
3
+ require 'test/unit'
4
+ gem 'mcmire-context'
5
+ require 'context'
6
+ gem 'mcmire-matchy'
7
+ require 'matchy'
8
+ require 'rr-matchy'
9
+
10
+ require 'rails/init'
11
+
12
+ class Test::Unit::TestCase
13
+ include RR::Adapters::TestUnit
14
+ include RRMatchy
15
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: after_timestamps
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Elliot Winkler
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-07 00:00:00 -06:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activerecord
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.2.6
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: mcmire-context
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.5.6
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: mcmire-matchy
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.4.1
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: rr
47
+ type: :development
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 0.10.5
54
+ version:
55
+ - !ruby/object:Gem::Dependency
56
+ name: rr-matchy
57
+ type: :development
58
+ version_requirement:
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 0.1.0
64
+ version:
65
+ description: Plugin for Ruby on Rails that gives you a way to add a callback to the ActiveRecord callback chain that will be executed right after the record's timestamp columns are set, but before the record is actually saved to the database. This is useful if you want to do something with the timestamps, such as defaulting another time column to created_at, or rolling back a timestamp by a certain amount.
66
+ email: elliot.winkler@gmail.com
67
+ executables: []
68
+
69
+ extensions: []
70
+
71
+ extra_rdoc_files:
72
+ - README.md
73
+ files:
74
+ - README.md
75
+ - Rakefile
76
+ - after_timestamps.gemspec
77
+ - lib/mcmire/after_timestamps.rb
78
+ - lib/mcmire/after_timestamps/version.rb
79
+ - rails/init.rb
80
+ - test/after_timestamps_test.rb
81
+ - test/helper.rb
82
+ has_rdoc: true
83
+ homepage: http://github.com/mcmire/after_timestamps
84
+ licenses: []
85
+
86
+ post_install_message:
87
+ rdoc_options:
88
+ - --charset=UTF-8
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: "0"
96
+ version:
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: "0"
102
+ version:
103
+ requirements: []
104
+
105
+ rubyforge_project:
106
+ rubygems_version: 1.3.5
107
+ signing_key:
108
+ specification_version: 3
109
+ summary: Rails plugin that provides an AR callback right after timestamps are set and before the record is saved
110
+ test_files:
111
+ - test/after_timestamps_test.rb
112
+ - test/helper.rb