seamusabshere-has_timestamps 1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/MIT-LICENSE +20 -0
- data/README +59 -0
- data/Rakefile +22 -0
- data/has_timestamps.gemspec +27 -0
- data/init.rb +4 -0
- data/install.rb +1 -0
- data/lib/has_timestamps.rb +58 -0
- data/lib/timestamp.rb +26 -0
- data/tasks/has_timestamps_tasks.rake +4 -0
- data/test/has_timestamps_test.rb +150 -0
- data/test/test_helper.rb +85 -0
- data/uninstall.rb +1 -0
- metadata +63 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 [name of plugin creator]
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
HasTimestamps
|
2
|
+
=================
|
3
|
+
|
4
|
+
Lets you timestamp models, like if you want to fake a CRM.
|
5
|
+
|
6
|
+
Note: it doesn't save objects automatically, so you have to run a "user.save" (etc.) when you're done timestamping.
|
7
|
+
|
8
|
+
Example
|
9
|
+
=======
|
10
|
+
|
11
|
+
class CreateTimestamps < ActiveRecord::Migration
|
12
|
+
def self.up
|
13
|
+
create_table(:timestamps) do |t|
|
14
|
+
t.integer :timestampable_id
|
15
|
+
t.string :timestampable_type
|
16
|
+
t.string :key
|
17
|
+
t.datetime :stamped_at
|
18
|
+
t.timestamps
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.down
|
23
|
+
drop_table(:timestamps)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
then...
|
28
|
+
|
29
|
+
class User < ActiveRecord::Base
|
30
|
+
has_timestamps
|
31
|
+
end
|
32
|
+
|
33
|
+
>> user.timestamps
|
34
|
+
=> []
|
35
|
+
>> user.timestamp!(:hailed)
|
36
|
+
=> Wed Dec 10 15:11:52 -0500 2008
|
37
|
+
>> user.timestamps
|
38
|
+
=> [#<Timestamp id: nil, timestampable_id: 14, timestampable_type: "User", key: "hailed", stamped_at: "2008-12-10 15:11:52", created_at: nil, updated_at: nil>]
|
39
|
+
>> user.timestamped?(:hailed)
|
40
|
+
=> true
|
41
|
+
>> user.save
|
42
|
+
=> true
|
43
|
+
>> user.timestamped?(:saluted)
|
44
|
+
=> false
|
45
|
+
>> user.timestamp!(:saluted)
|
46
|
+
=> Wed Dec 10 15:12:28 -0500 2008
|
47
|
+
>> user.timestamped?(:saluted)
|
48
|
+
=> true
|
49
|
+
>> user.timestamps[:saluted]
|
50
|
+
=> Wed Dec 10 15:12:28 -0500 2008
|
51
|
+
>> user.timestamps[:hailed]
|
52
|
+
=> Wed Dec 10 15:11:52 -0500 2008
|
53
|
+
|
54
|
+
Credits
|
55
|
+
=======
|
56
|
+
|
57
|
+
Thanks to Fingertips for inspiration.
|
58
|
+
|
59
|
+
Copyright (c) 2008 Seamus Abshere, released under the MIT license.
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Test the has_timestamps plugin.'
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.pattern = 'test/**/*_test.rb'
|
12
|
+
t.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Generate documentation for the has_timestamps plugin.'
|
16
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
17
|
+
rdoc.rdoc_dir = 'rdoc'
|
18
|
+
rdoc.title = 'HasTimestamps'
|
19
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
20
|
+
rdoc.rdoc_files.include('README')
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "has_timestamps"
|
3
|
+
s.version = "1.0"
|
4
|
+
s.date = "2008-12-29"
|
5
|
+
s.summary = "Rails plugin to add named timestamps to ActiveRecord models."
|
6
|
+
s.email = "seamus@abshere.net"
|
7
|
+
s.homepage = "http://github.com/seamusabshere/has_timestamps"
|
8
|
+
s.description = "has_timestamps is a Rails plugin that allows you to add named timestamps to ActiveRecord models without adding database columns."
|
9
|
+
s.has_rdoc = false
|
10
|
+
s.authors = "Seamus Abshere"
|
11
|
+
s.files = [
|
12
|
+
"has_timestamps.gemspec",
|
13
|
+
"init.rb",
|
14
|
+
"install.rb",
|
15
|
+
"lib/has_timestamps.rb",
|
16
|
+
"lib/timestamp.rb",
|
17
|
+
"MIT-LICENSE",
|
18
|
+
"Rakefile",
|
19
|
+
"README",
|
20
|
+
"tasks/has_timestamps_tasks.rake",
|
21
|
+
"uninstall.rb"
|
22
|
+
]
|
23
|
+
s.test_files = [
|
24
|
+
"test/has_timestamps_test.rb",
|
25
|
+
"test/test_helper.rb"
|
26
|
+
]
|
27
|
+
end
|
data/init.rb
ADDED
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Install hook code here
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module ActiveRecord #:nodoc:
|
2
|
+
module Acts #:nodoc:
|
3
|
+
module HasTimestamps
|
4
|
+
def self.included(base) #:nodoc:
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def has_timestamps(opts = {})
|
10
|
+
class_eval do
|
11
|
+
def save_or_destroy_timestamps
|
12
|
+
timestamps.each do |timestamp|
|
13
|
+
if timestamp.stamped_at.acts_like?(:time) or timestamp.stamped_at.is_a?(Date) or timestamp.stamped_at.is_a?(DateTime)
|
14
|
+
timestamp.save
|
15
|
+
elsif !timestamp.new_record?
|
16
|
+
timestamp.destroy
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
after_save :save_or_destroy_timestamps
|
21
|
+
|
22
|
+
def timestamp!(key)
|
23
|
+
timestamps[key.to_s] = Time.now
|
24
|
+
end
|
25
|
+
|
26
|
+
def timestamped?(key)
|
27
|
+
!timestamps[key.to_s].blank?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
has_many :timestamps, opts.merge(:as => :timestampable) do
|
32
|
+
def [](key)
|
33
|
+
fetch_timestamp(key).stamped_at
|
34
|
+
end
|
35
|
+
|
36
|
+
def []=(key, stamped_at)
|
37
|
+
fetch_timestamp(key).stamped_at = stamped_at
|
38
|
+
end
|
39
|
+
|
40
|
+
def find_by_key(key)
|
41
|
+
proxy_owner.timestamps.to_a.find { |timestamp| timestamp.key == key.to_s }
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def fetch_timestamp(key)
|
47
|
+
find_by_key(key) || build_timestamp(key)
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_timestamp(key)
|
51
|
+
build(:key => key.to_s)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/timestamp.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
class Timestamp < ActiveRecord::Base
|
2
|
+
class << self
|
3
|
+
def create_table
|
4
|
+
self.connection.create_table(:timestamps) do |t|
|
5
|
+
t.integer :timestampable_id
|
6
|
+
t.string :timestampable_type
|
7
|
+
t.string :key
|
8
|
+
t.datetime :stamped_at
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def drop_table
|
14
|
+
self.connection.drop_table(:timestamps)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
belongs_to :timestampable, :polymorphic => true
|
19
|
+
|
20
|
+
validates_presence_of :key, :timestampable_id, :timestampable_type
|
21
|
+
validates_uniqueness_of :key, :scope => [ :timestampable_id, :timestampable_type ]
|
22
|
+
|
23
|
+
def to_param
|
24
|
+
self.key
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
module SharedTests
|
4
|
+
def self.included(klass)
|
5
|
+
klass.class_eval do
|
6
|
+
it "should test if two times are virtually simultaneous" do
|
7
|
+
t1 = Time.at(946702800)
|
8
|
+
t2 = Time.at(946702800 + DELTA_IN_SECONDS)
|
9
|
+
t3 = Time.at(946702800 + DELTA_IN_SECONDS + 1)
|
10
|
+
assert_simultaneous(t1, t2)
|
11
|
+
assert_not_simultaneous(t1, t3)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should get timestamp with brackets" do
|
15
|
+
assert_equal @timestamp.stamped_at, @person.timestamps[@timestamp.key.to_sym]
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should get nil timestamp with brackets" do
|
19
|
+
assert_nil @person.timestamps[:gibberish]
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should get timestamp as a Time" do
|
23
|
+
assert @person.timestamps[@timestamp.key.to_sym].acts_like?(:time)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should query timestamp with brackets" do
|
27
|
+
assert_equal @person.timestamped?(@timestamp.key.to_sym), true
|
28
|
+
assert_equal @person.timestamped?(:gibberish), false
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should set timestamp" do
|
32
|
+
@person.timestamp!(:hailed)
|
33
|
+
@person.save
|
34
|
+
@person.reload
|
35
|
+
assert_simultaneous Time.now, @person.timestamps[:hailed]
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should reset timestamp" do
|
39
|
+
old_time = @person.timestamps[:saluted]
|
40
|
+
@person.timestamp!(:saluted)
|
41
|
+
@person.save
|
42
|
+
@person.reload
|
43
|
+
assert_not_simultaneous(old_time, @person.timestamps[:saluted])
|
44
|
+
assert_simultaneous Time.now, @person.timestamps[:saluted]
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should set timestamp manually" do
|
48
|
+
@person.timestamps[:hailed] = @another_time
|
49
|
+
@person.save
|
50
|
+
@person.reload
|
51
|
+
assert_simultaneous @another_time, @person.timestamps[:hailed]
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should set timestamp manually to Date" do
|
55
|
+
@person.timestamps[:hailed] = @another_time.to_date
|
56
|
+
@person.save
|
57
|
+
@person.reload
|
58
|
+
assert_simultaneous @another_time, @person.timestamps[:hailed]
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should set timestamp manually to DateTime" do
|
62
|
+
@person.timestamps[:hailed] = @another_time.to_datetime
|
63
|
+
@person.save
|
64
|
+
@person.reload
|
65
|
+
assert_simultaneous @another_time, @person.timestamps[:hailed]
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should set timestamp to nil" do
|
69
|
+
@person.timestamps[:hailed] = nil
|
70
|
+
@person.save
|
71
|
+
@person.reload
|
72
|
+
assert_nil @person.timestamps[:hailed]
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should reset timestamp manually" do
|
76
|
+
old_time = @person.timestamps[:saluted]
|
77
|
+
@person.timestamps[:saluted] = @another_time
|
78
|
+
@person.save
|
79
|
+
@person.reload
|
80
|
+
assert_not_simultaneous old_time, @person.timestamps[:saluted]
|
81
|
+
assert_simultaneous @another_time, @person.timestamps[:saluted]
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should reset timestamp to nil" do
|
85
|
+
@person.timestamps[:saluted] = nil
|
86
|
+
@person.save
|
87
|
+
@person.reload
|
88
|
+
assert_nil @person.timestamps[:saluted]
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should save timestamps" do
|
92
|
+
assert_difference('Timestamp.count', 1) do
|
93
|
+
@person.timestamp!(:hailed)
|
94
|
+
@person.save
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should destroy nil timestamps" do
|
99
|
+
assert_difference('Timestamp.count', -1) do
|
100
|
+
@person.timestamps[:saluted] = nil
|
101
|
+
@person.save
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should not save nil timestamps" do
|
106
|
+
assert_no_difference('Timestamp.count') do
|
107
|
+
@person.timestamps[:hailed] = nil
|
108
|
+
@person.save
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "HasTimestamps, when a timezone is not set" do
|
116
|
+
before do
|
117
|
+
HasTimestampsTest::Initializer.setup_database
|
118
|
+
@person = Person.first
|
119
|
+
@timestamp = Timestamp.first
|
120
|
+
@another_time = 3.days.ago.at_beginning_of_day
|
121
|
+
end
|
122
|
+
after do
|
123
|
+
HasTimestampsTest::Initializer.teardown_database
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should not have a timezone" do
|
127
|
+
assert_nil Time.zone
|
128
|
+
end
|
129
|
+
|
130
|
+
include SharedTests
|
131
|
+
end
|
132
|
+
|
133
|
+
describe "HasTimestamps, when a timezone is set" do
|
134
|
+
before do
|
135
|
+
HasTimestampsTest::Initializer.setup_database
|
136
|
+
Time.zone = 'Pacific Time (US & Canada)'
|
137
|
+
@person = Person.first
|
138
|
+
@timestamp = Timestamp.first
|
139
|
+
@another_time = 3.days.ago.at_beginning_of_day
|
140
|
+
end
|
141
|
+
after do
|
142
|
+
HasTimestampsTest::Initializer.teardown_database
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should have a timezone" do
|
146
|
+
assert_equal Time.zone.name, 'Pacific Time (US & Canada)'
|
147
|
+
end
|
148
|
+
|
149
|
+
include SharedTests
|
150
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
module HasTimestampsTest
|
2
|
+
module Initializer
|
3
|
+
VENDOR_RAILS = File.expand_path('../../../../rails', __FILE__)
|
4
|
+
OTHER_RAILS = File.expand_path('../../../rails', __FILE__)
|
5
|
+
PLUGIN_ROOT = File.expand_path('../../', __FILE__)
|
6
|
+
|
7
|
+
def self.rails_directory
|
8
|
+
if File.exist?(VENDOR_RAILS)
|
9
|
+
VENDOR_RAILS
|
10
|
+
elsif File.exist?(OTHER_RAILS)
|
11
|
+
OTHER_RAILS
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.load_dependencies
|
16
|
+
if rails_directory
|
17
|
+
$:.unshift(File.join(rails_directory, 'activesupport', 'lib'))
|
18
|
+
$:.unshift(File.join(rails_directory, 'activerecord', 'lib'))
|
19
|
+
else
|
20
|
+
require 'rubygems' rescue LoadError
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'activesupport'
|
24
|
+
require 'activerecord'
|
25
|
+
require 'active_support/testing/core_ext/test/unit/assertions'
|
26
|
+
|
27
|
+
require 'rubygems' rescue LoadError
|
28
|
+
|
29
|
+
require 'test/spec'
|
30
|
+
require File.join(PLUGIN_ROOT, 'init')
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.configure_database
|
34
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
|
35
|
+
ActiveRecord::Migration.verbose = false
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.setup_database
|
39
|
+
ActiveRecord::Schema.define do
|
40
|
+
create_table :people do |t|
|
41
|
+
t.string :name
|
42
|
+
end
|
43
|
+
create_table :timestamps do |t|
|
44
|
+
t.integer :timestampable_id
|
45
|
+
t.string :timestampable_type
|
46
|
+
t.string :key
|
47
|
+
t.datetime :stamped_at
|
48
|
+
t.timestamps
|
49
|
+
end
|
50
|
+
end
|
51
|
+
person_fixture = Person.create :name => 'Seamus'
|
52
|
+
person_fixture.timestamps[:saluted] = Time.now.years_ago(1).at_beginning_of_day
|
53
|
+
person_fixture.save
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.teardown_database
|
57
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
58
|
+
ActiveRecord::Base.connection.drop_table(table)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.start
|
63
|
+
load_dependencies
|
64
|
+
configure_database
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
HasTimestampsTest::Initializer.start
|
70
|
+
|
71
|
+
class Person < ActiveRecord::Base
|
72
|
+
has_timestamps
|
73
|
+
end
|
74
|
+
|
75
|
+
DELTA_IN_SECONDS = 5
|
76
|
+
|
77
|
+
def assert_simultaneous(t1, t2)
|
78
|
+
assert_in_delta t1, t2, DELTA_IN_SECONDS
|
79
|
+
end
|
80
|
+
|
81
|
+
def assert_not_simultaneous(t1, t2)
|
82
|
+
assert_raise(Test::Unit::AssertionFailedError) do
|
83
|
+
assert_simultaneous t1, t2
|
84
|
+
end
|
85
|
+
end
|
data/uninstall.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Uninstall hook code here
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: seamusabshere-has_timestamps
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "1.0"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Seamus Abshere
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-12-29 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: has_timestamps is a Rails plugin that allows you to add named timestamps to ActiveRecord models without adding database columns.
|
17
|
+
email: seamus@abshere.net
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- has_timestamps.gemspec
|
26
|
+
- init.rb
|
27
|
+
- install.rb
|
28
|
+
- lib/has_timestamps.rb
|
29
|
+
- lib/timestamp.rb
|
30
|
+
- MIT-LICENSE
|
31
|
+
- Rakefile
|
32
|
+
- README
|
33
|
+
- tasks/has_timestamps_tasks.rake
|
34
|
+
- uninstall.rb
|
35
|
+
has_rdoc: false
|
36
|
+
homepage: http://github.com/seamusabshere/has_timestamps
|
37
|
+
post_install_message:
|
38
|
+
rdoc_options: []
|
39
|
+
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: "0"
|
47
|
+
version:
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: "0"
|
53
|
+
version:
|
54
|
+
requirements: []
|
55
|
+
|
56
|
+
rubyforge_project:
|
57
|
+
rubygems_version: 1.2.0
|
58
|
+
signing_key:
|
59
|
+
specification_version: 2
|
60
|
+
summary: Rails plugin to add named timestamps to ActiveRecord models.
|
61
|
+
test_files:
|
62
|
+
- test/has_timestamps_test.rb
|
63
|
+
- test/test_helper.rb
|