maintain 0.2.10 → 0.2.11
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/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
|
|