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 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.10
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
@@ -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
@@ -91,13 +91,17 @@ module Maintain
91
91
  if block_given?
92
92
  method = block
93
93
  end
94
- hooks[state.to_sym] ||= {}
95
- hooks[state.to_sym][event.to_sym] ||= []
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][event.to_sym].push(method_hash)
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
- if method_free?(boolean_method)
141
- # Define it if'n it don't already exit! These are just proxies - so Foo.maintains(:state) { state :awesome }
142
- # will now have Foo.new.awesome?. But that's really just a proxy for Foo.new.state.awesome?
143
- # So they're just shortcuts for brevity's sake.
144
- maintainee.class_eval <<-EOC
145
- def #{boolean_method}
146
- #{@attribute}.#{boolean_method}
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
@@ -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 == $1.to_sym
82
+ return self == check.to_sym
83
83
  elsif aggregates = @state.aggregates[check]
84
84
  self.class.class_eval <<-EOC
85
85
  def #{method}
@@ -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
- describe "accessors" do
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
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 :each do
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
@@ -19,6 +19,7 @@ describe Maintain do
19
19
  @maintainer = MaintainTest.new
20
20
  @maintainer.permissions.edit?.should be_true
21
21
  @maintainer.permissions.delete?.should be_true
22
+ @maintainer.permissions.update?.should_not be_true
22
23
  end
23
24
  end
24
25
 
@@ -74,9 +74,21 @@ describe Maintain do
74
74
  state :closed
75
75
  end
76
76
  maintainer = MaintainTest.new
77
- maintainer.state.new?.should be_true
78
- maintainer.state.overdue?.should be_false
79
- maintainer.state.closed?.should be_false
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: 3
4
+ hash: 1
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 10
10
- version: 0.2.10
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-03 00:00:00 -04:00
18
+ date: 2011-06-23 00:00:00 -04:00
19
19
  default_executable:
20
20
  dependencies: []
21
21