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 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