ar_after_timestamps 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/README.md +58 -0
- data/Rakefile +59 -0
- data/ar_after_timestamps.gemspec +63 -0
- data/lib/mcmire/ar_after_timestamps.rb +101 -0
- data/rails/init.rb +7 -0
- data/test/ar_after_timestamps_test.rb +61 -0
- data/test/helper.rb +11 -0
- metadata +112 -0
data/.gitignore
ADDED
data/README.md
ADDED
@@ -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.
|
data/Rakefile
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
require File.dirname(__FILE__) + "/lib/mcmire/ar_after_timestamps"
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'jeweler'
|
8
|
+
Jeweler::Tasks.new do |gem|
|
9
|
+
gem.version = Mcmire::ARAfterTimestamps::VERSION
|
10
|
+
gem.name = "ar_after_timestamps"
|
11
|
+
gem.summary = %Q{Rails gem that provides an AR callback right after timestamps are set and before the record is saved}
|
12
|
+
gem.description = %Q{Gem that plugs into 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/ar_after_timestamps"
|
16
|
+
gem.add_dependency "activerecord", "< 3.0"
|
17
|
+
gem.add_development_dependency "mcmire-protest", "~> 0.2.4"
|
18
|
+
gem.add_development_dependency "mcmire-matchy", "~> 0.4.1"
|
19
|
+
gem.add_development_dependency "mcmire-mocha", "~> 0.9.8"
|
20
|
+
gem.add_development_dependency "mocha-protest-integration"
|
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 << File.dirname(__FILE__) << '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
|
+
begin
|
53
|
+
require 'yard'
|
54
|
+
YARD::Rake::YardocTask.new
|
55
|
+
rescue LoadError
|
56
|
+
task :yardoc do
|
57
|
+
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
58
|
+
end
|
59
|
+
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{ar_after_timestamps}
|
8
|
+
s.version = "0.2.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-09}
|
13
|
+
s.description = %q{Gem that plugs into 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
|
+
".gitignore",
|
20
|
+
"README.md",
|
21
|
+
"Rakefile",
|
22
|
+
"ar_after_timestamps.gemspec",
|
23
|
+
"lib/mcmire/ar_after_timestamps.rb",
|
24
|
+
"rails/init.rb",
|
25
|
+
"test/ar_after_timestamps_test.rb",
|
26
|
+
"test/helper.rb"
|
27
|
+
]
|
28
|
+
s.homepage = %q{http://github.com/mcmire/ar_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 gem that provides an AR callback right after timestamps are set and before the record is saved}
|
33
|
+
s.test_files = [
|
34
|
+
"test/ar_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>, ["< 3.0"])
|
44
|
+
s.add_development_dependency(%q<mcmire-protest>, ["~> 0.2.4"])
|
45
|
+
s.add_development_dependency(%q<mcmire-matchy>, ["~> 0.4.1"])
|
46
|
+
s.add_development_dependency(%q<mcmire-mocha>, ["~> 0.9.8"])
|
47
|
+
s.add_development_dependency(%q<mocha-protest-integration>, [">= 0"])
|
48
|
+
else
|
49
|
+
s.add_dependency(%q<activerecord>, ["< 3.0"])
|
50
|
+
s.add_dependency(%q<mcmire-protest>, ["~> 0.2.4"])
|
51
|
+
s.add_dependency(%q<mcmire-matchy>, ["~> 0.4.1"])
|
52
|
+
s.add_dependency(%q<mcmire-mocha>, ["~> 0.9.8"])
|
53
|
+
s.add_dependency(%q<mocha-protest-integration>, [">= 0"])
|
54
|
+
end
|
55
|
+
else
|
56
|
+
s.add_dependency(%q<activerecord>, ["< 3.0"])
|
57
|
+
s.add_dependency(%q<mcmire-protest>, ["~> 0.2.4"])
|
58
|
+
s.add_dependency(%q<mcmire-matchy>, ["~> 0.4.1"])
|
59
|
+
s.add_dependency(%q<mcmire-mocha>, ["~> 0.9.8"])
|
60
|
+
s.add_dependency(%q<mocha-protest-integration>, [">= 0"])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
@@ -0,0 +1,101 @@
|
|
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 ARAfterTimestamps
|
75
|
+
VERSION = "0.2.0"
|
76
|
+
|
77
|
+
def self.included(klass)
|
78
|
+
klass.class_eval do
|
79
|
+
alias_method_chain :create_without_timestamps, :after_timestamps
|
80
|
+
alias_method_chain :update_without_timestamps, :after_timestamps
|
81
|
+
define_callbacks :after_timestamps_on_create, :after_timestamps_on_update
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def after_timestamps_on_create() end
|
86
|
+
def after_timestamps_on_update() end
|
87
|
+
|
88
|
+
private
|
89
|
+
# Override create_with_timestamps to call the after_timestamps_on_create callback
|
90
|
+
def create_without_timestamps_with_after_timestamps
|
91
|
+
return false if callback(:after_timestamps_on_create) == false
|
92
|
+
create_without_timestamps_without_after_timestamps
|
93
|
+
end
|
94
|
+
|
95
|
+
# Same thing, only for update_with_timestamps
|
96
|
+
def update_without_timestamps_with_after_timestamps
|
97
|
+
return false if callback(:after_timestamps_on_update) == false
|
98
|
+
update_without_timestamps_without_after_timestamps
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
ActiveRecord::Base.establish_connection(
|
4
|
+
"adapter" => "sqlite3",
|
5
|
+
"database" => ":memory:"
|
6
|
+
)
|
7
|
+
|
8
|
+
ActiveRecord::Migration.suppress_messages do
|
9
|
+
ActiveRecord::Schema.define do
|
10
|
+
create_table :posts do |t|
|
11
|
+
t.timestamps
|
12
|
+
t.datetime :posted_at, :null => false
|
13
|
+
t.string :formatted_created_at
|
14
|
+
t.integer :timestamp
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Post < ActiveRecord::Base
|
20
|
+
after_timestamps_on_create :set_posted_at_to_created_at
|
21
|
+
after_timestamps_on_create {|post| post.formatted_created_at = post.created_at.strftime("%b/%Y-%d %D %H%M..%S") }
|
22
|
+
after_timestamps_on_update :rollback_created_at
|
23
|
+
after_timestamps_on_update {|post| post.timestamp = post.created_at.to_i }
|
24
|
+
|
25
|
+
def set_posted_at_to_created_at
|
26
|
+
self.posted_at = self.created_at
|
27
|
+
end
|
28
|
+
def rollback_created_at
|
29
|
+
self.posted_at -= 2.days
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
Protest.context("ar_after_timestamps") do
|
34
|
+
test "after_timestamps_on_create symbol is called on create" do
|
35
|
+
Time.stubs(:now).returns(Time.local(2009))
|
36
|
+
post = Post.new
|
37
|
+
post.save!
|
38
|
+
post.posted_at.should == Time.local(2009)
|
39
|
+
end
|
40
|
+
|
41
|
+
test "after_timestamps_on_create proc is called on create" do
|
42
|
+
Time.stubs(:now).returns(Time.local(2009))
|
43
|
+
post = Post.new
|
44
|
+
post.save!
|
45
|
+
post.formatted_created_at.should == "Jan/2009-01 01/01/09 0000..00"
|
46
|
+
end
|
47
|
+
|
48
|
+
test "after_timestamps_on_update symbol is called on update" do
|
49
|
+
Time.stubs(:now).returns(Time.local(2009))
|
50
|
+
post = Post.create!
|
51
|
+
post.save!
|
52
|
+
post.posted_at.should == Time.local(2008, 12, 30)
|
53
|
+
end
|
54
|
+
|
55
|
+
test "after_timestamp_on_update proc is called on update" do
|
56
|
+
Time.stubs(:now).returns(Time.local(2009))
|
57
|
+
post = Post.create!
|
58
|
+
post.save!
|
59
|
+
post.timestamp.should == 1230789600
|
60
|
+
end
|
61
|
+
end
|
data/test/helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ar_after_timestamps
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Elliot Winkler
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-01-09 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: "3.0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: mcmire-protest
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.2.4
|
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: mcmire-mocha
|
47
|
+
type: :development
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.9.8
|
54
|
+
version:
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: mocha-protest-integration
|
57
|
+
type: :development
|
58
|
+
version_requirement:
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
description: Gem that plugs into 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
|
+
- .gitignore
|
75
|
+
- README.md
|
76
|
+
- Rakefile
|
77
|
+
- ar_after_timestamps.gemspec
|
78
|
+
- lib/mcmire/ar_after_timestamps.rb
|
79
|
+
- rails/init.rb
|
80
|
+
- test/ar_after_timestamps_test.rb
|
81
|
+
- test/helper.rb
|
82
|
+
has_rdoc: true
|
83
|
+
homepage: http://github.com/mcmire/ar_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 gem that provides an AR callback right after timestamps are set and before the record is saved
|
110
|
+
test_files:
|
111
|
+
- test/ar_after_timestamps_test.rb
|
112
|
+
- test/helper.rb
|