maintain 0.2.10 → 0.2.11
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +18 -0
- data/VERSION +1 -1
- data/lib/maintain/backend/active_record.rb +18 -2
- data/lib/maintain/backend/base.rb +6 -0
- data/lib/maintain/backend.rb +2 -2
- data/lib/maintain/maintainer.rb +25 -16
- data/lib/maintain/value.rb +1 -1
- data/spec/active_record_spec.rb +74 -11
- data/spec/bitwise_spec.rb +1 -0
- data/spec/comparing_state_spec.rb +15 -3
- metadata +4 -4
data/README.markdown
CHANGED
@@ -150,6 +150,24 @@ relational database - or you could implement a single bitmask column:
|
|
150
150
|
|
151
151
|
# foo.state will boil happily down to an integer when you store it.
|
152
152
|
|
153
|
+
You can also set multiple defaults on bitmasks, just in case you're defaults involve some complicated mix of options:
|
154
|
+
|
155
|
+
class Foo
|
156
|
+
extend Maintain
|
157
|
+
maintains :state, :bitmask => true do
|
158
|
+
state :new, 1, :default => true
|
159
|
+
state :old, 2
|
160
|
+
state :borrowed, 3, :default => true
|
161
|
+
state :blue, 4
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
foo = Foo.new
|
166
|
+
foo.new? #=> true
|
167
|
+
foo.old? #=> false
|
168
|
+
foo.borrowed? #=> true
|
169
|
+
foo.blue? #=> false
|
170
|
+
|
153
171
|
Named Scopes
|
154
172
|
-
|
155
173
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.11
|
@@ -3,20 +3,36 @@ module Maintain
|
|
3
3
|
class ActiveRecord < Maintain::Backend::Base
|
4
4
|
def aggregate(maintainee, name, attribute, states)
|
5
5
|
# named_scope will handle the array of states as "IN" in SQL
|
6
|
-
state(maintainee, name, attribute, states)
|
6
|
+
state(maintainee, name, attribute, states, false)
|
7
|
+
end
|
8
|
+
|
9
|
+
def on(maintainee, attribute, event, state, method, options)
|
10
|
+
attribute_check = "#{attribute}#{"_was" if event == :exit}_#{state}?"
|
11
|
+
maintainee.before_save method, :if => lambda {|instance|
|
12
|
+
instance.send("#{attribute}_changed?") && instance.send(attribute_check) &&
|
13
|
+
(!options[:if] || (options[:if].is_a?(Proc) && instance.instance_eval(&options[:unless])) || instance.send(options[:if])) &&
|
14
|
+
(!options[:unless] || !(options[:unless].is_a?(Proc) && instance.instance_eval(&options[:unless])) || !instance.send(options[:unless]))
|
15
|
+
}
|
7
16
|
end
|
8
17
|
|
9
18
|
def read(instance, attribute)
|
10
19
|
instance.read_attribute(attribute)
|
11
20
|
end
|
12
21
|
|
13
|
-
def state(maintainee, name, attribute, value)
|
22
|
+
def state(maintainee, name, attribute, value, dirty = true)
|
14
23
|
conditions = {:conditions => {attribute => value}}
|
15
24
|
if defined?(::ActiveRecord::VERSION) && ::ActiveRecord::VERSION::STRING >= '3'
|
16
25
|
maintainee.scope name, conditions
|
17
26
|
else
|
18
27
|
maintainee.named_scope name, conditions
|
19
28
|
end
|
29
|
+
if dirty
|
30
|
+
maintainee.class_eval <<-dirty_tracker
|
31
|
+
def #{attribute}_was_#{name}?
|
32
|
+
#{attribute}_was == self.class.maintainers[:#{attribute}].value(self).value_for(:#{name})
|
33
|
+
end
|
34
|
+
dirty_tracker
|
35
|
+
end
|
20
36
|
end
|
21
37
|
|
22
38
|
def write(instance, attribute, value)
|
@@ -1,10 +1,16 @@
|
|
1
1
|
module Maintain
|
2
2
|
module Backend
|
3
3
|
class Base
|
4
|
+
attr_reader :maintainer
|
5
|
+
|
4
6
|
def aggregate(maintainee, attribute, name, options, states)
|
5
7
|
require_method :aggregate
|
6
8
|
end
|
7
9
|
|
10
|
+
def initialize(maintainer)
|
11
|
+
@maintainer = maintainer
|
12
|
+
end
|
13
|
+
|
8
14
|
def read(instance, attribute)
|
9
15
|
require_method :read
|
10
16
|
end
|
data/lib/maintain/backend.rb
CHANGED
@@ -25,11 +25,11 @@ module Maintain
|
|
25
25
|
def build(back_end, maintainer)
|
26
26
|
back_end = back_end.to_s.split('_').map(&:capitalize).join('')
|
27
27
|
if constants.include? back_end.to_s
|
28
|
-
const_get(back_end.to_sym).new
|
28
|
+
const_get(back_end.to_sym).new(maintainer)
|
29
29
|
else
|
30
30
|
begin
|
31
31
|
back_end = const_missing(back_end)
|
32
|
-
back_end.new
|
32
|
+
back_end.new(maintainer)
|
33
33
|
rescue
|
34
34
|
end
|
35
35
|
end
|
data/lib/maintain/maintainer.rb
CHANGED
@@ -91,13 +91,17 @@ module Maintain
|
|
91
91
|
if block_given?
|
92
92
|
method = block
|
93
93
|
end
|
94
|
-
|
95
|
-
|
96
|
-
method_hash = {:method => method}.merge(options)
|
97
|
-
if old_definition = hooks[state.to_sym][event.to_sym].find{|hook| hook[:method] == method}
|
98
|
-
old_definition.merge!(method_hash)
|
94
|
+
if back_end && back_end.respond_to?(:on)
|
95
|
+
back_end.on(maintainee, @attribute, event, state, method, options)
|
99
96
|
else
|
100
|
-
hooks[state.to_sym]
|
97
|
+
hooks[state.to_sym] ||= {}
|
98
|
+
hooks[state.to_sym][event.to_sym] ||= []
|
99
|
+
method_hash = {:method => method}.merge(options)
|
100
|
+
if old_definition = hooks[state.to_sym][event.to_sym].find{|hook| hook[:method] == method}
|
101
|
+
old_definition.merge!(method_hash)
|
102
|
+
else
|
103
|
+
hooks[state.to_sym][event.to_sym].push(method_hash)
|
104
|
+
end
|
101
105
|
end
|
102
106
|
end
|
103
107
|
|
@@ -134,19 +138,24 @@ module Maintain
|
|
134
138
|
default(name)
|
135
139
|
end
|
136
140
|
|
141
|
+
if options.has_key?(:enter)
|
142
|
+
on :enter, name.to_sym, options.delete(:enter)
|
143
|
+
end
|
144
|
+
|
145
|
+
if options.has_key?(:exit)
|
146
|
+
on :exit, name.to_sym, options.delete(:exit)
|
147
|
+
end
|
148
|
+
|
137
149
|
# Now we're going to add proxies to test for state. These methods only get added if a
|
138
150
|
# method of their name doesn't already exist.
|
139
151
|
boolean_method = "#{name}?"
|
140
|
-
|
141
|
-
|
142
|
-
#
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
end
|
148
|
-
EOC
|
149
|
-
end
|
152
|
+
# Override any attribute_state? methods, because those we need for hooks...
|
153
|
+
maintainee.class_eval <<-EOC
|
154
|
+
def #{@attribute}_#{boolean_method}
|
155
|
+
#{@attribute}.#{boolean_method}
|
156
|
+
end
|
157
|
+
#{"alias :#{boolean_method} :#{@attribute}_#{boolean_method}" if method_free?(boolean_method)}
|
158
|
+
EOC
|
150
159
|
end
|
151
160
|
|
152
161
|
def states
|
data/lib/maintain/value.rb
CHANGED
@@ -79,7 +79,7 @@ module Maintain
|
|
79
79
|
end
|
80
80
|
EOC
|
81
81
|
# Calling `method` on ourselves fails. Something to do w/subclasses. Meh.
|
82
|
-
return self ==
|
82
|
+
return self == check.to_sym
|
83
83
|
elsif aggregates = @state.aggregates[check]
|
84
84
|
self.class.class_eval <<-EOC
|
85
85
|
def #{method}
|
data/spec/active_record_spec.rb
CHANGED
@@ -19,19 +19,22 @@ if proceed
|
|
19
19
|
it "should automatically be extended" do
|
20
20
|
ActiveRecord::Base.should respond_to(:maintain)
|
21
21
|
end
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
22
|
+
|
23
|
+
before :each do
|
24
|
+
ActiveRecord::Base.establish_connection({:adapter => 'sqlite3', :database => ':memory:', :pool => 5, :timeout => 5000})
|
25
|
+
class ::ActiveMaintainTest < ActiveRecord::Base; end
|
26
|
+
silence_stream(STDOUT) do
|
27
|
+
ActiveRecord::Schema.define do
|
28
|
+
create_table :active_maintain_tests, :force => true do |t|
|
29
|
+
t.string :status
|
30
|
+
t.integer :permissions
|
32
31
|
end
|
33
32
|
end
|
33
|
+
end
|
34
|
+
end
|
34
35
|
|
36
|
+
describe "accessors" do
|
37
|
+
before :all do
|
35
38
|
ActiveMaintainTest.maintain :status do
|
36
39
|
state :new, :default => true
|
37
40
|
state :old
|
@@ -75,7 +78,7 @@ if proceed
|
|
75
78
|
end
|
76
79
|
|
77
80
|
describe "bitmasks" do
|
78
|
-
before :
|
81
|
+
before :all do
|
79
82
|
ActiveMaintainTest.maintain :permissions, :bitmask => true do
|
80
83
|
state :add, 0
|
81
84
|
state :delete, 1
|
@@ -114,7 +117,67 @@ if proceed
|
|
114
117
|
end
|
115
118
|
end
|
116
119
|
|
120
|
+
describe "hooks" do
|
121
|
+
before :all do
|
122
|
+
ActiveMaintainTest.maintain :status do
|
123
|
+
state :new, :default => true
|
124
|
+
state :old
|
125
|
+
state :foo
|
126
|
+
state :bar
|
127
|
+
aggregate :everything, :as => [:new, :old, :foo, :bar]
|
128
|
+
aggregate :fakes, :as => [:foo, :bar]
|
129
|
+
|
130
|
+
on :enter, :old, :do_something
|
131
|
+
on :exit, :foo, :do_something_else
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
ActiveMaintainTest.class_eval do
|
136
|
+
def do_something
|
137
|
+
# Do... something?
|
138
|
+
end
|
139
|
+
|
140
|
+
def do_something_else
|
141
|
+
# Do something else!
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should not send hooks immediately on attribute setting" do
|
147
|
+
active_maintain_test = ActiveMaintainTest.new
|
148
|
+
active_maintain_test.should_not_receive(:do_something)
|
149
|
+
active_maintain_test.status = :old
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should send hooks when a record is saved" do
|
153
|
+
active_maintain_test = ActiveMaintainTest.new
|
154
|
+
active_maintain_test.should_receive(:do_something)
|
155
|
+
active_maintain_test.status = :old
|
156
|
+
active_maintain_test.save
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should send :exit hooks when a record is saved after a value is exited from" do
|
160
|
+
active_maintain_test = ActiveMaintainTest.new
|
161
|
+
active_maintain_test.status = :foo
|
162
|
+
active_maintain_test.save
|
163
|
+
active_maintain_test.should_receive(:do_something_else)
|
164
|
+
active_maintain_test.status = :new
|
165
|
+
active_maintain_test.save
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
117
169
|
describe "named_scopes" do
|
170
|
+
before :all do
|
171
|
+
ActiveMaintainTest.maintain :status do
|
172
|
+
state :new, :default => true
|
173
|
+
state :old
|
174
|
+
state :foo
|
175
|
+
state :bar
|
176
|
+
aggregate :everything, :as => [:new, :old, :foo, :bar]
|
177
|
+
aggregate :fakes, :as => [:foo, :bar]
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
118
181
|
it "should create named_scopes for all states" do
|
119
182
|
ActiveMaintainTest.should respond_to(:old)
|
120
183
|
ActiveMaintainTest.old.should respond_to(:each)
|
data/spec/bitwise_spec.rb
CHANGED
@@ -74,9 +74,21 @@ describe Maintain do
|
|
74
74
|
state :closed
|
75
75
|
end
|
76
76
|
maintainer = MaintainTest.new
|
77
|
-
maintainer.
|
78
|
-
maintainer.
|
79
|
-
maintainer.
|
77
|
+
maintainer.new?.should be_true
|
78
|
+
maintainer.overdue?.should be_false
|
79
|
+
maintainer.closed?.should be_false
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should work with an attribute name prefix, too!" do
|
83
|
+
MaintainTest.maintain :state, :default => :new do
|
84
|
+
state :new
|
85
|
+
state :overdue
|
86
|
+
state :closed
|
87
|
+
end
|
88
|
+
maintainer = MaintainTest.new
|
89
|
+
maintainer.state_new?.should be_true
|
90
|
+
maintainer.state_overdue?.should be_false
|
91
|
+
maintainer.state_closed?.should be_false
|
80
92
|
end
|
81
93
|
|
82
94
|
it "should not override pre-existing methods" do
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: maintain
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 1
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 2
|
9
|
-
-
|
10
|
-
version: 0.2.
|
9
|
+
- 11
|
10
|
+
version: 0.2.11
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Flip Sasser
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-06-
|
18
|
+
date: 2011-06-23 00:00:00 -04:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|