transient 1.0.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/.document +5 -0
- data/.gitignore +23 -0
- data/History.txt +3 -0
- data/LICENSE +20 -0
- data/README.rdoc +82 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/lib/date_time_extensions.rb +45 -0
- data/lib/transient.rb +10 -0
- data/lib/transient/active_record_extensions.rb +146 -0
- data/script/console +11 -0
- data/spec/database.yml +3 -0
- data/spec/date_time_extensions_spec.rb +41 -0
- data/spec/single_active_transient_shared_spec.rb +11 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +48 -0
- data/spec/transient/active_record_extensions_spec.rb +5 -0
- data/spec/transient_shared_spec.rb +63 -0
- data/spec/transient_spec.rb +66 -0
- data/transient.gemspec +74 -0
- metadata +109 -0
data/.document
ADDED
data/.gitignore
ADDED
data/History.txt
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Jason Harrelson
|
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.rdoc
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
= transient
|
2
|
+
|
3
|
+
http://github.com/midas/transient
|
4
|
+
|
5
|
+
|
6
|
+
== DESCRIPTION
|
7
|
+
|
8
|
+
Provides an API for making any ActiveRecord object transient.
|
9
|
+
|
10
|
+
|
11
|
+
== FEATURES
|
12
|
+
|
13
|
+
* Convenience methods for transient objects, ie. current?, expired?, expire!, etc.
|
14
|
+
* Callbacks for expiring current 'active' record prior to creating new one where certain configured fields
|
15
|
+
match. See the Transient::ActiveRecordExtensions::InstanceMethods docs for more information.
|
16
|
+
|
17
|
+
|
18
|
+
== REQUIREMENTS
|
19
|
+
|
20
|
+
* ActiveRecord >= 2.3
|
21
|
+
* ActiveSupport >= 2.3
|
22
|
+
|
23
|
+
|
24
|
+
== INSTALL
|
25
|
+
|
26
|
+
gem sources -a http://gemcutter.org
|
27
|
+
sudo gem install transient
|
28
|
+
|
29
|
+
|
30
|
+
== INSTALL FOR RAILS
|
31
|
+
|
32
|
+
Add to environment file:
|
33
|
+
|
34
|
+
config.gem "transient", :version => '1.0.0', :source => 'http://gemcutter.org'
|
35
|
+
|
36
|
+
Run:
|
37
|
+
|
38
|
+
sudo rake:gems:install
|
39
|
+
|
40
|
+
|
41
|
+
== USAGE
|
42
|
+
|
43
|
+
Call the macro from your ActiveRecord descendant class:
|
44
|
+
|
45
|
+
class User < ActiveRecord::Base
|
46
|
+
acts_as_transient
|
47
|
+
end
|
48
|
+
|
49
|
+
Now you can use all of the convenience methods, expired?, current?, expire!, etc. See the InstanceMethods docs for more information.
|
50
|
+
|
51
|
+
Call the macro with a list of fields to use to lookup the current active record and expire it before saving the new one:
|
52
|
+
|
53
|
+
class ContactNumber < ActiveRecord::Base
|
54
|
+
acts_as_transient :single_active => %w(location user_id)
|
55
|
+
end
|
56
|
+
|
57
|
+
This will try to find an existing contact number record where the location and user_id match the record being saved and expire
|
58
|
+
the existing record before saving the new record.
|
59
|
+
|
60
|
+
|
61
|
+
== Note on Patches/Pull Requests
|
62
|
+
|
63
|
+
* Fork the project.
|
64
|
+
* Make your feature addition or bug fix.
|
65
|
+
* Add tests for it. This is important so I don't break it in a
|
66
|
+
future version unintentionally.
|
67
|
+
* Commit, do not mess with rakefile, version, or history.
|
68
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
69
|
+
* Send me a pull request. Bonus points for topic branches.
|
70
|
+
|
71
|
+
|
72
|
+
== LICENSE
|
73
|
+
|
74
|
+
Copyright (c) 2009 C. Jason Harrelson (midas)
|
75
|
+
|
76
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
77
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
78
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
79
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
80
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
81
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
82
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
|
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "transient"
|
8
|
+
gem.summary = %Q{Provides an API for making any ActiveRecord object transient.}
|
9
|
+
gem.description = %Q{Provides an API for making any ActiveRecord object transient. In addition, provides functionality for models where only a single instance of the model can be current at one time.}
|
10
|
+
gem.email = "jason@lookforwardenterprises.com"
|
11
|
+
gem.homepage = "http://github.com/midas/transient"
|
12
|
+
gem.authors = ["C. Jason Harrelson (midas)"]
|
13
|
+
gem.add_dependency "activerecord", ">= 2.3"
|
14
|
+
gem.add_dependency "activesupport", ">= 2.3"
|
15
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
16
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
17
|
+
end
|
18
|
+
Jeweler::GemcutterTasks.new
|
19
|
+
rescue LoadError
|
20
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'spec/rake/spectask'
|
24
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
25
|
+
spec.libs << 'lib' << 'spec'
|
26
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
27
|
+
end
|
28
|
+
|
29
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
30
|
+
spec.libs << 'lib' << 'spec'
|
31
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
32
|
+
spec.rcov = true
|
33
|
+
end
|
34
|
+
|
35
|
+
task :spec => :check_dependencies
|
36
|
+
|
37
|
+
task :default => :spec
|
38
|
+
|
39
|
+
require 'rake/rdoctask'
|
40
|
+
Rake::RDocTask.new do |rdoc|
|
41
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
42
|
+
|
43
|
+
rdoc.rdoc_dir = 'rdoc'
|
44
|
+
rdoc.title = "transient #{version}"
|
45
|
+
rdoc.rdoc_files.include('README*')
|
46
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
47
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class DateTime
|
2
|
+
def self.beginning_of
|
3
|
+
DateTime.parse( "0000-01-01T00:00:00+00:00" )
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.end_of
|
7
|
+
DateTime.parse( "9999-12-31T00:00:00+00:00" )
|
8
|
+
end
|
9
|
+
|
10
|
+
def time?
|
11
|
+
return !(hour == 0 && min == 0 && sec == 0 && sec_fraction == 0)
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_standard_s
|
15
|
+
return time? ? strftime( "%B %d, %Y %I:%M %p" ) : to_date.to_standard_s
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Date
|
20
|
+
def self.beginning_of
|
21
|
+
DateTime.beginning_of.to_date
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.end_of
|
25
|
+
DateTime.end_of.to_date
|
26
|
+
end
|
27
|
+
|
28
|
+
def time?
|
29
|
+
false
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_standard_s
|
33
|
+
strftime( "%B %d, %Y" )
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Time
|
38
|
+
def time?
|
39
|
+
return !(hour == 0 && min == 0 && sec == 0)
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_standard_s
|
43
|
+
return time? ? to_datetime.to_standard_s : to_date.to_standard_s
|
44
|
+
end
|
45
|
+
end
|
data/lib/transient.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
require 'date_time_extensions'
|
4
|
+
require 'transient/active_record_extensions'
|
5
|
+
|
6
|
+
module Transient
|
7
|
+
VERSION = '1.0.0'
|
8
|
+
end
|
9
|
+
|
10
|
+
ActiveRecord::Base.send( :include, Transient::ActiveRecordExtensions ) if defined?( ActiveRecord::Base )
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require File.expand_path( File.dirname(__FILE__) ) + '/../date_time_extensions'
|
2
|
+
|
3
|
+
module Transient
|
4
|
+
module ActiveRecordExtensions
|
5
|
+
def self.included( base ) #:nodoc:
|
6
|
+
base.extend ActsMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
module ActsMethods
|
10
|
+
def acts_as_transient( *args )
|
11
|
+
include InstanceMethods unless included_modules.include?( InstanceMethods )
|
12
|
+
options = args.extract_options!
|
13
|
+
options.merge!( :single_active => false ) unless options[:single_active]
|
14
|
+
#options.merge!( :check_exists => false ) unless options[:check_exists]
|
15
|
+
if options[:single_active] != false
|
16
|
+
include SingleActive unless included_modules.include?( SingleActive )
|
17
|
+
class_inheritable_accessor :transient_options
|
18
|
+
self.transient_options = options
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module InstanceMethods
|
24
|
+
def self.included( includee ) #:nodoc:
|
25
|
+
#includee.validates_presence_of :effective_at
|
26
|
+
#includee.validates_presence_of :expiring_at
|
27
|
+
|
28
|
+
#includee.default(:effective_at) { DateTime.beginning_of }
|
29
|
+
#includee.default(:expiring_at) { DateTime.end_of }
|
30
|
+
|
31
|
+
includee.named_scope :current, lambda { { :conditions => ["effective_at <= ? AND (expiring_at IS NULL OR expiring_at > ?)",
|
32
|
+
DateTime.now.utc, DateTime.now.utc] } }
|
33
|
+
|
34
|
+
public
|
35
|
+
|
36
|
+
# Validates this record's effective dates occur in correct sequence (ie. effective_at is before
|
37
|
+
# expiring_at).
|
38
|
+
#
|
39
|
+
def validate
|
40
|
+
return unless self.effective_at && self.expiring_at
|
41
|
+
|
42
|
+
unless self.effective_at.to_datetime <= self.expiring_at.to_datetime
|
43
|
+
self.errors.add( :effective_through, "effective at should be earlier than expiring at" )
|
44
|
+
end
|
45
|
+
|
46
|
+
super
|
47
|
+
end
|
48
|
+
|
49
|
+
# The date this record becomes effective. Returns DateTime.beginning_of for records that have a
|
50
|
+
# nil value in the database.
|
51
|
+
#
|
52
|
+
def effective_at
|
53
|
+
return self[:effective_at].blank? ? DateTime.beginning_of : self[:effective_at]
|
54
|
+
end
|
55
|
+
|
56
|
+
# The date this record expires. Returns DateTime.end_of for records that have a
|
57
|
+
# nil value in the database.
|
58
|
+
#
|
59
|
+
def expiring_at
|
60
|
+
return self[:expiring_at].blank? ? DateTime.end_of : self[:expiring_at]
|
61
|
+
end
|
62
|
+
|
63
|
+
# The range this record is effective wihtin.
|
64
|
+
#
|
65
|
+
def effective_through
|
66
|
+
self.effective_at.to_datetime..self.expiring_at.to_datetime
|
67
|
+
end
|
68
|
+
|
69
|
+
# Sets the range this record is effective within.
|
70
|
+
#
|
71
|
+
def effective_through=( value )
|
72
|
+
self[:effective_at] = value.begin.to_datetime
|
73
|
+
self[:expiring_at] = value.end.to_datetime
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns true if this record's exiring_at date is equivalent to DateTime.end_of, otherwise false.
|
77
|
+
#
|
78
|
+
def permanent?
|
79
|
+
self.expiring_at.to_datetime == DateTime.end_of
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns true if this is currently effective (ie. now is within the effective range), otherwise false.
|
83
|
+
#
|
84
|
+
def effective?
|
85
|
+
self.effective_through === DateTime.now
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns true if this record is expired, otherwise false.
|
89
|
+
#
|
90
|
+
def expired?
|
91
|
+
self.expiring_at.to_datetime < DateTime.now
|
92
|
+
end
|
93
|
+
|
94
|
+
# Expires and saves this record.
|
95
|
+
#
|
96
|
+
def expire!
|
97
|
+
before_expire! if self.respond_to?( :before_expire! )
|
98
|
+
self.expiring_at = DateTime.now
|
99
|
+
self.save
|
100
|
+
after_expire! if self.respond_to?( :after_expire! )
|
101
|
+
end
|
102
|
+
|
103
|
+
# Returns true if this record is not yet effective, but will become effective at some point in the
|
104
|
+
# future, otherwise false.
|
105
|
+
#
|
106
|
+
def future?
|
107
|
+
self.effective_at.to_datetime > DateTime.now
|
108
|
+
end
|
109
|
+
|
110
|
+
# Alias for effective?.
|
111
|
+
#
|
112
|
+
def current?
|
113
|
+
self.effective?
|
114
|
+
end
|
115
|
+
|
116
|
+
# Alias for expired?.
|
117
|
+
#
|
118
|
+
def past?
|
119
|
+
self.expired?
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
module SingleActive
|
125
|
+
def self.included( includee ) #:nodoc:
|
126
|
+
includee.before_create :expire_current_active
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
def expire_current_active
|
131
|
+
#if self.transient_options[:check_exists]
|
132
|
+
# exists_conditions = {}
|
133
|
+
# self.transient_options[:check_exists].each { |attr| exists_conditions.merge!( attr.to_sym => attributes[attr] ) }
|
134
|
+
# #cur = self.class.current.find( :first, :conditions => exists_conditions )
|
135
|
+
# return true if self.class.current.exists?( exists_conditions )
|
136
|
+
#end
|
137
|
+
|
138
|
+
conditions = {}
|
139
|
+
self.transient_options[:single_active].each { |attr| conditions.merge!( attr.to_sym => attributes[attr] ) }
|
140
|
+
old = self.class.current.find( :first, :conditions => conditions )
|
141
|
+
old.expire! unless old.nil?
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
data/script/console
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r 'active_record' -r '#{File.dirname(__FILE__) + '/../lib/transient.rb'}'"
|
9
|
+
|
10
|
+
puts "Loading transient gem"
|
11
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/spec/database.yml
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe Date do
|
4
|
+
it "should define the beginning of time" do
|
5
|
+
Date.should respond_to( :beginning_of )
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should define the end of time" do
|
9
|
+
Date.should respond_to( :end_of )
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should format itself to a standard string" do
|
13
|
+
now = Date.new
|
14
|
+
now.to_standard_s.should == now.to_datetime.to_standard_s
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe DateTime do
|
19
|
+
it "should define the beginning of time" do
|
20
|
+
DateTime.should respond_to( :beginning_of )
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should define the end of time" do
|
24
|
+
DateTime.should respond_to( :end_of )
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should format itself to the standard string format" do
|
28
|
+
now = DateTime.now
|
29
|
+
now.to_standard_s.should == now.strftime( "%B %d, %Y %I:%M %p" )
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "all date and time classes" do
|
34
|
+
it "should all report the same beginning of time" do
|
35
|
+
DateTime.beginning_of.to_datetime.should == Date.beginning_of.to_datetime
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should all report the same end of time" do
|
39
|
+
DateTime.end_of.to_datetime.should == Date.end_of.to_datetime
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require File.expand_path( File.dirname(__FILE__) + '/spec_helper' )
|
2
|
+
|
3
|
+
shared_examples_for "Any transient that is single active" do
|
4
|
+
it "should agree that the InstanceMethods module is included" do
|
5
|
+
@klass.included_modules.include?( Transient::ActiveRecordExtensions::InstanceMethods ).should be_true
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should agree that the SingleActive module is included" do
|
9
|
+
@klass.included_modules.include?( Transient::ActiveRecordExtensions::SingleActive ).should be_true
|
10
|
+
end
|
11
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
require 'rubygems'
|
4
|
+
require 'active_record'
|
5
|
+
require 'transient'
|
6
|
+
require 'spec'
|
7
|
+
require 'spec/autorun'
|
8
|
+
|
9
|
+
Spec::Runner.configure do |config|
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
ActiveRecord::Base.configurations = YAML::load( IO.read( File.dirname(__FILE__) + '/database.yml' ) )
|
14
|
+
ActiveRecord::Base.establish_connection( 'test' )
|
15
|
+
|
16
|
+
ActiveRecord::Schema.define :version => 1 do
|
17
|
+
create_table :users, :force => true do |t|
|
18
|
+
t.string :name, :limit => 50
|
19
|
+
t.datetime :effective_at
|
20
|
+
t.datetime :expiring_at
|
21
|
+
end
|
22
|
+
|
23
|
+
create_table :contact_numbers, :force => true do |t|
|
24
|
+
t.string :number, :limit => 35
|
25
|
+
t.string :location, :limit => 20
|
26
|
+
t.datetime :effective_at
|
27
|
+
t.datetime :expiring_at
|
28
|
+
end
|
29
|
+
|
30
|
+
create_table :addresses, :force => true do |t|
|
31
|
+
t.string :street, :limit => 75
|
32
|
+
t.string :location, :limit => 20
|
33
|
+
t.datetime :effective_at
|
34
|
+
t.datetime :expiring_at
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class User < ActiveRecord::Base
|
39
|
+
acts_as_transient
|
40
|
+
end
|
41
|
+
|
42
|
+
class ContactNumber < ActiveRecord::Base
|
43
|
+
acts_as_transient :single_active => %w(location)
|
44
|
+
end
|
45
|
+
|
46
|
+
class Address < ActiveRecord::Base
|
47
|
+
acts_as_transient :single_active => %w(location), :check_exists => %w(street location)
|
48
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require File.expand_path( File.dirname(__FILE__) + '/spec_helper' )
|
2
|
+
|
3
|
+
shared_examples_for "Any transient" do
|
4
|
+
it "should be Transient" do
|
5
|
+
@instance.should respond_to(:effective_at)
|
6
|
+
@instance.should respond_to(:expiring_at)
|
7
|
+
@instance.should respond_to(:effective_through)
|
8
|
+
@instance.should respond_to(:permanent?)
|
9
|
+
@instance.should respond_to(:effective?)
|
10
|
+
@instance.should respond_to(:expired?)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should report permanence" do
|
14
|
+
@instance.effective_at = DateTime.beginning_of
|
15
|
+
@instance.expiring_at = DateTime.end_of
|
16
|
+
|
17
|
+
@instance.should be_permanent
|
18
|
+
@instance.should be_effective
|
19
|
+
@instance.should be_current
|
20
|
+
@instance.should_not be_expired
|
21
|
+
@instance.should_not be_past
|
22
|
+
@instance.should_not be_future
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should report temporariness" do
|
26
|
+
@instance.effective_at = 5.minutes.ago
|
27
|
+
@instance.expiring_at = 5.minutes.from_now
|
28
|
+
|
29
|
+
@instance.should_not be_permanent
|
30
|
+
@instance.should be_effective
|
31
|
+
@instance.should be_current
|
32
|
+
@instance.should_not be_expired
|
33
|
+
@instance.should_not be_past
|
34
|
+
@instance.should_not be_future
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should report expiration" do
|
38
|
+
@instance.effective_at = 10.minutes.ago
|
39
|
+
@instance.expiring_at = 5.minutes.ago
|
40
|
+
|
41
|
+
@instance.should_not be_permanent
|
42
|
+
@instance.should_not be_effective
|
43
|
+
@instance.should be_expired
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should report upcomingness" do
|
47
|
+
@instance.effective_at = 5.minutes.from_now
|
48
|
+
@instance.expiring_at = 10.minutes.from_now
|
49
|
+
|
50
|
+
@instance.should_not be_permanent
|
51
|
+
@instance.should_not be_effective
|
52
|
+
@instance.should_not be_current
|
53
|
+
@instance.should_not be_expired
|
54
|
+
@instance.should_not be_past
|
55
|
+
@instance.should be_future
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should invoke callbacks around expire!" do
|
59
|
+
@instance.should_receive(:before_expire!)
|
60
|
+
@instance.should_receive(:after_expire!)
|
61
|
+
@instance.expire!
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/transient_shared_spec')
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/single_active_transient_shared_spec')
|
4
|
+
|
5
|
+
describe "Transient" do
|
6
|
+
describe "having ActiveRecord extensions" do
|
7
|
+
it "should respond to phone_number" do
|
8
|
+
ActiveRecord::Base.respond_to?( :acts_as_transient ).should be_true
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "having models descending from ActiveRecord" do
|
13
|
+
describe "that are not single active" do
|
14
|
+
before(:each) do
|
15
|
+
@instance = User.new( :name => 'John Smith', :effective_at => (DateTime.now - 1.days), :expiring_at => DateTime.end_of )
|
16
|
+
@instance.save!
|
17
|
+
end
|
18
|
+
|
19
|
+
it_should_behave_like "Any transient"
|
20
|
+
|
21
|
+
it "should agree that the InstanceMethods module is included" do
|
22
|
+
User.included_modules.include?( Transient::ActiveRecordExtensions::InstanceMethods ).should be_true
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should agree that the SingleActive module is not included" do
|
26
|
+
User.included_modules.include?( Transient::ActiveRecordExtensions::SingleActive ).should be_false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "that are single active" do
|
31
|
+
before(:each) do
|
32
|
+
@klass = ContactNumber
|
33
|
+
@instance = @klass.new( :number => '012345678901', :location => 'home', :effective_at => (DateTime.now - 1.days) )
|
34
|
+
@instance.save!
|
35
|
+
end
|
36
|
+
|
37
|
+
it_should_behave_like "Any transient"
|
38
|
+
it_should_behave_like "Any transient that is single active"
|
39
|
+
|
40
|
+
it "should expire the current active before saving a new one" do
|
41
|
+
@new_contact = ContactNumber.new( :number => '019876543210', :location => 'home', :effective_at => DateTime.now )
|
42
|
+
@new_contact.save!
|
43
|
+
@instance.reload
|
44
|
+
@instance.expired?.should be_true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "that are single active with check exists" do
|
49
|
+
before(:each) do
|
50
|
+
@klass = Address
|
51
|
+
@instance = @klass.new( :street => '26 street', :location => 'home', :effective_at => (DateTime.now - 1.days) )
|
52
|
+
@instance.save!
|
53
|
+
end
|
54
|
+
|
55
|
+
it_should_behave_like "Any transient"
|
56
|
+
it_should_behave_like "Any transient that is single active"
|
57
|
+
|
58
|
+
it "should expire the current active before saving a new one" do
|
59
|
+
@new_address = Address.new( :street => '27 street', :location => 'home', :effective_at => DateTime.now )
|
60
|
+
@new_address.save!
|
61
|
+
@instance.reload
|
62
|
+
@instance.expired?.should be_true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/transient.gemspec
ADDED
@@ -0,0 +1,74 @@
|
|
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{transient}
|
8
|
+
s.version = "1.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["C. Jason Harrelson (midas)"]
|
12
|
+
s.date = %q{2009-12-27}
|
13
|
+
s.description = %q{Provides an API for making any ActiveRecord object transient. In addition, provides functionality for models where only a single instance of the model can be current at one time.}
|
14
|
+
s.email = %q{jason@lookforwardenterprises.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"History.txt",
|
23
|
+
"LICENSE",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"lib/date_time_extensions.rb",
|
28
|
+
"lib/transient.rb",
|
29
|
+
"lib/transient/active_record_extensions.rb",
|
30
|
+
"script/console",
|
31
|
+
"spec/database.yml",
|
32
|
+
"spec/date_time_extensions_spec.rb",
|
33
|
+
"spec/single_active_transient_shared_spec.rb",
|
34
|
+
"spec/spec.opts",
|
35
|
+
"spec/spec_helper.rb",
|
36
|
+
"spec/transient/active_record_extensions_spec.rb",
|
37
|
+
"spec/transient_shared_spec.rb",
|
38
|
+
"spec/transient_spec.rb",
|
39
|
+
"transient.gemspec"
|
40
|
+
]
|
41
|
+
s.homepage = %q{http://github.com/midas/transient}
|
42
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
43
|
+
s.require_paths = ["lib"]
|
44
|
+
s.rubygems_version = %q{1.3.5}
|
45
|
+
s.summary = %q{Provides an API for making any ActiveRecord object transient.}
|
46
|
+
s.test_files = [
|
47
|
+
"spec/date_time_extensions_spec.rb",
|
48
|
+
"spec/single_active_transient_shared_spec.rb",
|
49
|
+
"spec/spec_helper.rb",
|
50
|
+
"spec/transient/active_record_extensions_spec.rb",
|
51
|
+
"spec/transient_shared_spec.rb",
|
52
|
+
"spec/transient_spec.rb"
|
53
|
+
]
|
54
|
+
|
55
|
+
if s.respond_to? :specification_version then
|
56
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
57
|
+
s.specification_version = 3
|
58
|
+
|
59
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
60
|
+
s.add_runtime_dependency(%q<activerecord>, [">= 2.3"])
|
61
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 2.3"])
|
62
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
63
|
+
else
|
64
|
+
s.add_dependency(%q<activerecord>, [">= 2.3"])
|
65
|
+
s.add_dependency(%q<activesupport>, [">= 2.3"])
|
66
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
67
|
+
end
|
68
|
+
else
|
69
|
+
s.add_dependency(%q<activerecord>, [">= 2.3"])
|
70
|
+
s.add_dependency(%q<activesupport>, [">= 2.3"])
|
71
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: transient
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- C. Jason Harrelson (midas)
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-12-27 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: "2.3"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: activesupport
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "2.3"
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: rspec
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.2.9
|
44
|
+
version:
|
45
|
+
description: Provides an API for making any ActiveRecord object transient. In addition, provides functionality for models where only a single instance of the model can be current at one time.
|
46
|
+
email: jason@lookforwardenterprises.com
|
47
|
+
executables: []
|
48
|
+
|
49
|
+
extensions: []
|
50
|
+
|
51
|
+
extra_rdoc_files:
|
52
|
+
- LICENSE
|
53
|
+
- README.rdoc
|
54
|
+
files:
|
55
|
+
- .document
|
56
|
+
- .gitignore
|
57
|
+
- History.txt
|
58
|
+
- LICENSE
|
59
|
+
- README.rdoc
|
60
|
+
- Rakefile
|
61
|
+
- VERSION
|
62
|
+
- lib/date_time_extensions.rb
|
63
|
+
- lib/transient.rb
|
64
|
+
- lib/transient/active_record_extensions.rb
|
65
|
+
- script/console
|
66
|
+
- spec/database.yml
|
67
|
+
- spec/date_time_extensions_spec.rb
|
68
|
+
- spec/single_active_transient_shared_spec.rb
|
69
|
+
- spec/spec.opts
|
70
|
+
- spec/spec_helper.rb
|
71
|
+
- spec/transient/active_record_extensions_spec.rb
|
72
|
+
- spec/transient_shared_spec.rb
|
73
|
+
- spec/transient_spec.rb
|
74
|
+
- transient.gemspec
|
75
|
+
has_rdoc: true
|
76
|
+
homepage: http://github.com/midas/transient
|
77
|
+
licenses: []
|
78
|
+
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options:
|
81
|
+
- --charset=UTF-8
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: "0"
|
89
|
+
version:
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: "0"
|
95
|
+
version:
|
96
|
+
requirements: []
|
97
|
+
|
98
|
+
rubyforge_project:
|
99
|
+
rubygems_version: 1.3.5
|
100
|
+
signing_key:
|
101
|
+
specification_version: 3
|
102
|
+
summary: Provides an API for making any ActiveRecord object transient.
|
103
|
+
test_files:
|
104
|
+
- spec/date_time_extensions_spec.rb
|
105
|
+
- spec/single_active_transient_shared_spec.rb
|
106
|
+
- spec/spec_helper.rb
|
107
|
+
- spec/transient/active_record_extensions_spec.rb
|
108
|
+
- spec/transient_shared_spec.rb
|
109
|
+
- spec/transient_spec.rb
|