maintain 0.1.9 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.9
1
+ 0.2.1
@@ -0,0 +1,4 @@
1
+ require 'autotest/fsevent'
2
+ require 'autotest/growl'
3
+
4
+ Autotest.add_discovery { "rspec2" }
@@ -0,0 +1,27 @@
1
+ module Maintain
2
+ module Backend
3
+ class ActiveRecord < Maintain::Backend::Base
4
+ def aggregate(maintainee, name, attribute, states)
5
+ # named_scope will handle the array of states as "IN" in SQL
6
+ state(maintainee, name, attribute, states)
7
+ end
8
+
9
+ def read(instance, attribute)
10
+ instance.attributes[attribute.to_s]
11
+ end
12
+
13
+ def state(maintainee, name, attribute, value)
14
+ conditions = {:conditions => {attribute => value}}
15
+ if defined?(::ActiveRecord::VERSION) && ::ActiveRecord::VERSION::STRING >= '3'
16
+ maintainee.scope name, conditions
17
+ else
18
+ maintainee.named_scope name, conditions
19
+ end
20
+ end
21
+
22
+ def write(instance, attribute, value)
23
+ instance.send(:write_attribute, attribute, value)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,22 @@
1
+ module Maintain
2
+ module Backend
3
+ class Base
4
+ def aggregate(maintainee, attribute, name, options, states)
5
+ require_method :aggregate
6
+ end
7
+
8
+ def read(instance, attribute)
9
+ require_method :read
10
+ end
11
+
12
+ def write(instance, attribute, value)
13
+ require_method :write
14
+ end
15
+
16
+ private
17
+ def require_method(method_name)
18
+ raise "You need to implement the ##{method_name} method in #{self.class.name}"
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,35 @@
1
+ require 'maintain/backend/base'
2
+
3
+ module Maintain
4
+ module Backend
5
+ class << self
6
+ def build(back_end, maintainer)
7
+ back_end = back_end.split('_').map(&:capitalize).join('')
8
+ if constants.include? back_end.to_s
9
+ const_get(back_end.to_sym).new
10
+ else
11
+ begin
12
+ back_end = const_missing(back_end)
13
+ back_end.new
14
+ rescue
15
+ end
16
+ end
17
+ end
18
+
19
+ def const_missing(constant)
20
+ underscore_constant = constant.to_s.dup
21
+ underscore_constant.gsub!(/::/, '/')
22
+ underscore_constant.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
23
+ underscore_constant.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
24
+ underscore_constant.tr!("-", "_")
25
+ underscore_constant.downcase!
26
+ begin
27
+ require("maintain/backend/#{underscore_constant}")
28
+ const_get(constant)
29
+ rescue
30
+ super
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,6 +1,8 @@
1
1
  # encoding: UTF-8
2
2
  module Maintain
3
3
  class Maintainer
4
+ attr_reader :back_end
5
+
4
6
  def aggregate(name, options)
5
7
  if options.is_a?(Hash) && options.has_key?(:as)
6
8
  options = options[:as]
@@ -20,13 +22,8 @@ module Maintain
20
22
  EOC
21
23
  end
22
24
  # Now define the state
23
- if @active_record && method_free?(name, true)
24
- conditions = {:conditions => {@attribute => options.map{|value| states[value][:value].is_a?(Symbol) ? states[value][:value].to_s : states[value][:value] }}}
25
- if defined?(ActiveRecord::VERSION) && ActiveRecord::VERSION::STRING >= "3"
26
- maintainee.scope name, conditions
27
- else
28
- maintainee.named_scope name, conditions
29
- end
25
+ if back_end && method_free?(name, true)
26
+ back_end.aggregate(maintainee, name, @attribute, options.map{|value| states[value][:value].is_a?(Symbol) ? states[value][:value].to_s : states[value][:value] })
30
27
  end
31
28
  end
32
29
 
@@ -42,8 +39,12 @@ module Maintain
42
39
  !!@bitmask
43
40
  end
44
41
 
45
- def default(state)
46
- @default = state
42
+ def default(state = nil)
43
+ if state
44
+ @default = state
45
+ else
46
+ @default
47
+ end
47
48
  end
48
49
 
49
50
  def default?
@@ -60,10 +61,12 @@ module Maintain
60
61
  end
61
62
  end
62
63
 
63
- def initialize(maintainee, attribute, active_record = false, options = {})
64
+ def initialize(maintainee, attribute, options = {})
64
65
  @maintainee = maintainee.name
65
66
  @attribute = attribute.to_sym
66
- @active_record = !!active_record
67
+ if back_end = options.delete(:back_end)
68
+ @back_end = Maintain::Backend.build(back_end, self)
69
+ end
67
70
  options.each do |key, value|
68
71
  self.send(key, value)
69
72
  end
@@ -120,13 +123,8 @@ module Maintain
120
123
  value ||= name
121
124
  states[name] = {:compare_value => !bitmask? && value.is_a?(Integer) ? value : @increment, :value => value}
122
125
  @increment += 1
123
- if @active_record && !maintainee.respond_to?(name)
124
- conditions = {:conditions => {@attribute => value.is_a?(Symbol) ? value.to_s : value}}
125
- if defined?(ActiveRecord::VERSION) && ActiveRecord::VERSION::STRING >= "3"
126
- maintainee.scope name, conditions
127
- else
128
- maintainee.named_scope name, conditions
129
- end
126
+ if !maintainee.respond_to?(name) && back_end
127
+ back_end.state maintainee, name, @attribute, value.is_a?(Symbol) ? value.to_s : value
130
128
  end
131
129
 
132
130
  # Now we're going to add proxies to test for state. These methods only get added if a
@@ -148,13 +146,14 @@ module Maintain
148
146
  @states ||= {}
149
147
  end
150
148
 
151
- def value(initial = nil)
149
+ def value(instance, initial = nil)
150
+ initial = (back_end && back_end.read(instance, @attribute)) || initial || @default
152
151
  if bitmask?
153
- BitmaskValue.new(self, initial || @default || 0)
152
+ BitmaskValue.new(self, initial || 0)
154
153
  elsif integer?
155
- IntegerValue.new(self, initial || @default)
154
+ IntegerValue.new(self, initial)
156
155
  else
157
- Value.new(self, initial || @default)
156
+ Value.new(self, initial)
158
157
  end
159
158
  end
160
159
 
@@ -164,7 +163,7 @@ module Maintain
164
163
  end
165
164
 
166
165
  def maintainee
167
- @maintainee.split('::').inject(Object) {|mod, const| mod.const_get(const) }
166
+ @_maintainee ||= @maintainee.split('::').inject(Object) {|mod, const| mod.const_get(const) }
168
167
  end
169
168
 
170
169
  def method_free?(method_name, class_method = false)
data/lib/maintain.rb CHANGED
@@ -5,6 +5,7 @@ module Maintain
5
5
  # We're not really interested in loading anything into memory if we don't need to,
6
6
  # so Maintainer, Value, and the Value subclasses are ignored until they're needed.
7
7
  autoload(:Maintainer, 'maintain/maintainer')
8
+ autoload(:Backend, 'maintain/backend')
8
9
  autoload(:Value, 'maintain/value')
9
10
  autoload(:BitmaskValue, 'maintain/bitmask_value')
10
11
  autoload(:IntegerValue, 'maintain/integer_value')
@@ -39,13 +40,12 @@ module Maintain
39
40
  active_record = superclass == ActiveRecord::Base
40
41
  superclass = superclass.superclass
41
42
  end
42
- else
43
- active_record = false
43
+ options[:back_end] = 'active_record' if active_record
44
44
  end
45
45
 
46
46
  # Create an instance of the maintainer class. It handles all of the state
47
47
  # configuration, hooking, aggregation, named_scoping, etc.
48
- maintainer = Maintainer.new(self, attribute, active_record, options)
48
+ maintainer = Maintainer.new(self, attribute, options)
49
49
  if block_given?
50
50
  maintainer.instance_eval(&block)
51
51
  end
@@ -54,37 +54,38 @@ module Maintain
54
54
  # on if you've already defined them. This is because they're how Maintain works.
55
55
  class_eval <<-EOC, __FILE__
56
56
  def #{attribute}=(value)
57
- # If we can find the maintainer on this attribute, we'll use it to set values.
58
- if maintainer = self.class.maintainers[#{attribute.to_sym.inspect}]
57
+ # Find the maintainer on this attribute so we can use it to set values.
58
+ if maintainer = self.class.maintainers[:#{attribute}]
59
+ changed = #{attribute} != value
59
60
  # Run the exit hook if we're changing the value
60
- maintainer.hook(:exit, #{attribute}.name, self)
61
+ maintainer.hook(:exit, #{attribute}.name, self) if changed
61
62
 
62
63
  # Then set the value itself. Maintainer::State will return the value you set,
63
64
  # so if we're setting to nil we get rid of the attribute entirely - it's not
64
65
  # needed and we want the getter to return nil in that case.
65
- # unless
66
- #{attribute}.set_value(value)#{%{
66
+ #{attribute}.set_value(value)
67
67
 
68
- # If this is ActiveRecord::Base or a subclass of it, we'll make sure calling the
69
- # setter writes a DB-friendly value.
70
- write_attribute(#{attribute.to_s.inspect}, @#{attribute} ? @#{attribute}.value.to_s : @#{attribute})
71
- } if active_record}
68
+ # Allow the back end to write values in an ORM-specific way
69
+ if maintainer.back_end
70
+ maintainer.back_end.write(self, :#{attribute}, value)
71
+ end
72
72
 
73
- # Last but not least, run the enter hooks for the new value - cause that's how we
74
- # do.
75
- maintainer.hook(:enter, #{attribute}.name, self) if @#{attribute}
76
- else
77
- # If we can't find a maintainer for this attribute, make our best effort to do what
78
- # attr_accessor does - set the instance variable.
79
- @#{attribute} = value#{%{
80
-
81
- # ... and on ActiveRecord::Base, we'll also write the attribute like a normal setter.
82
- write_attribute(:#{attribute}, @#{attribute})} if active_record}
73
+ # Last but not least, run the enter hooks for the new value - cause that's how
74
+ # we do.
75
+ maintainer.hook(:enter, #{attribute}.name, self) if changed
83
76
  end
84
77
  end
85
78
 
86
79
  def #{attribute}
87
- @#{attribute} ||= self.class.maintainers[#{attribute.to_sym.inspect}].value#{"(read_attribute(:#{attribute}))" if active_record}
80
+ if maintainer = self.class.maintainers[:#{attribute}]
81
+ if @#{attribute}
82
+ @#{attribute}
83
+ else
84
+ @#{attribute} = maintainer.value(self)
85
+ end
86
+ else
87
+ @#{attribute}
88
+ end
88
89
  end
89
90
  EOC
90
91
 
@@ -92,14 +93,6 @@ module Maintain
92
93
  # and we'll also modify it instead of replacing it outright, so subclasses or mixins can extend functionality
93
94
  # without replacing it.
94
95
  maintainers[attribute.to_sym] = maintainer
95
- #
96
- # unless respond_to?(attribute)
97
- # class_eval <<-EOC
98
- # def #{attribute}
99
- # maintainers[#{attribute.to_sym.inspect}]
100
- # end
101
- # EOC
102
- # end
103
96
  end
104
97
  alias :maintains :maintain
105
98
 
@@ -110,7 +103,7 @@ module Maintain
110
103
  if File.file?(version_path = File.join(File.dirname(__FILE__), '..', 'VERSION'))
111
104
  VERSION = File.read(version_path).strip
112
105
  else
113
- VERSION = '0.0.1'
106
+ VERSION = '0.2.0'
114
107
  end
115
108
  end
116
109
 
data/maintain.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{maintain}
8
- s.version = "0.1.9"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Flip Sasser"]
12
- s.date = %q{2010-05-06}
12
+ s.date = %q{2011-02-03}
13
13
  s.description = %q{
14
14
  Maintain is a simple state machine mixin for Ruby objects. It supports comparisons, bitmasks,
15
15
  and hooks that really work. It can be used for multiple attributes and will always do its best to
@@ -20,47 +20,24 @@ Gem::Specification.new do |s|
20
20
  "README.markdown"
21
21
  ]
22
22
  s.files = [
23
- ".gitignore",
24
- "README.markdown",
25
23
  "Rakefile",
26
24
  "VERSION",
27
25
  "lib/maintain.rb",
26
+ "lib/maintain/backend.rb",
27
+ "lib/maintain/backend/active_record.rb",
28
+ "lib/maintain/backend/base.rb",
28
29
  "lib/maintain/bitmask_value.rb",
29
30
  "lib/maintain/integer_value.rb",
30
31
  "lib/maintain/maintainer.rb",
31
32
  "lib/maintain/value.rb",
32
- "maintain.gemspec",
33
- "spec/active_record_spec.rb",
34
- "spec/aggregates_spec.rb",
35
- "spec/bitwise_spec.rb",
36
- "spec/comparing_state_spec.rb",
37
- "spec/defining_states_spec.rb",
38
- "spec/hooks_spec.rb",
39
- "spec/integer_spec.rb",
40
- "spec/maintain_spec.rb",
41
- "spec/object_spec.rb",
42
- "spec/proxy_spec.rb",
43
- "spec/setting_state_spec.rb",
44
- "spec/spec.opts"
33
+ "maintain.gemspec"
45
34
  ]
46
35
  s.homepage = %q{http://github.com/flipsasser/maintain}
47
36
  s.rdoc_options = ["--charset=UTF-8"]
48
37
  s.require_paths = ["lib"]
49
38
  s.rubygems_version = %q{1.3.6}
50
39
  s.summary = %q{A Ruby state machine that lets your code do the driving}
51
- s.test_files = [
52
- "spec/active_record_spec.rb",
53
- "spec/aggregates_spec.rb",
54
- "spec/bitwise_spec.rb",
55
- "spec/comparing_state_spec.rb",
56
- "spec/defining_states_spec.rb",
57
- "spec/hooks_spec.rb",
58
- "spec/integer_spec.rb",
59
- "spec/maintain_spec.rb",
60
- "spec/object_spec.rb",
61
- "spec/proxy_spec.rb",
62
- "spec/setting_state_spec.rb"
63
- ]
40
+ s.test_files = Dir["spec/**/*"]
64
41
 
65
42
  if s.respond_to? :specification_version then
66
43
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
@@ -4,7 +4,7 @@
4
4
  proceed = false
5
5
  begin
6
6
  require 'rubygems'
7
- gem 'activerecord', '2.3.5'
7
+ gem 'activerecord', '>= 2.3.5'
8
8
  require 'active_record'
9
9
  proceed = true
10
10
  rescue Gem::LoadError, LoadError
@@ -0,0 +1,87 @@
1
+ # # Some specs to check against ActiveRecord conflicts. Rails tends to blow
2
+ # # shit up when you build it outside of Rails. We'll see how this goes...
3
+ #
4
+ # proceed = false
5
+ # begin
6
+ # require 'rubygems'
7
+ # gem 'datamapper'
8
+ # require 'datamapper'
9
+ # proceed = true
10
+ # rescue Gem::LoadError, LoadError
11
+ # puts 'Not testing DataMapper (unavailable)'
12
+ # end
13
+ #
14
+ # if proceed = false
15
+ # # Use load to have it evaluate the ActiveRecord::Base extension logic again, in the event
16
+ # # that we've already done that with a previous test.
17
+ # load 'lib/maintain.rb'
18
+ # describe Maintain, "Datamapper::Resource" do
19
+ # it "should automatically be extended" do
20
+ # ActiveRecord::Base.should respond_to(:maintain)
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
+ # end
31
+ # end
32
+ # end
33
+ #
34
+ # ActiveMaintainTest.maintain :status do
35
+ # state :new, :default => true
36
+ # state :old
37
+ # state :foo
38
+ # state :bar
39
+ # aggregate :everything, :as => [:new, :old, :foo, :bar]
40
+ # aggregate :fakes, :as => [:foo, :bar]
41
+ # end
42
+ # end
43
+ #
44
+ # it "should default to 'new'" do
45
+ # ActiveMaintainTest.new.status.should == 'new'
46
+ # ActiveMaintainTest.new.status.should == :new
47
+ # end
48
+ #
49
+ # it "should allow us to update its status to 'old'" do
50
+ # active_maintain_test = ActiveMaintainTest.new(:status => 'old')
51
+ # active_maintain_test.status.should == 'old'
52
+ # lambda {
53
+ # active_maintain_test.save!
54
+ # }.should_not raise_error
55
+ # ActiveMaintainTest.first.status.should == 'old'
56
+ # end
57
+ #
58
+ # it "should return the correct name when told to" do
59
+ # active_maintain_test = ActiveMaintainTest.create(:status => 'old')
60
+ # ActiveMaintainTest.first.status.name.should == 'old'
61
+ # end
62
+ # end
63
+ #
64
+ # describe "named_scopes" do
65
+ # it "should create named_scopes for all states" do
66
+ # ActiveMaintainTest.should respond_to(:old)
67
+ # ActiveMaintainTest.old.should respond_to(:each)
68
+ # end
69
+ #
70
+ # it "should create named_scopes for all aggregates" do
71
+ # ActiveMaintainTest.should respond_to(:everything)
72
+ # ActiveMaintainTest.everything.should respond_to(:each)
73
+ # end
74
+ #
75
+ # it "should return the correct collections on aggregates" do
76
+ # ActiveMaintainTest.destroy_all
77
+ # one = ActiveMaintainTest.create(:status => :foo)
78
+ # two = ActiveMaintainTest.create(:status => :bar)
79
+ # three = ActiveMaintainTest.create(:status => :new)
80
+ # four = ActiveMaintainTest.create(:status => :old)
81
+ # ActiveMaintainTest.fakes.should == [one, two]
82
+ # ActiveMaintainTest.everything.should == [one, two, three, four]
83
+ # end
84
+ #
85
+ # end
86
+ # end
87
+ # end
data/spec/hooks_spec.rb CHANGED
@@ -27,7 +27,7 @@ describe Maintain, "hooks" do
27
27
  state :new
28
28
  state :old
29
29
  on :enter, :new, :new_entered
30
- on :exit, :old do
30
+ on :enter, :old do
31
31
  self.old_entered
32
32
  end
33
33
  end
@@ -35,9 +35,8 @@ describe Maintain, "hooks" do
35
35
  maintain = MaintainTest.new
36
36
  maintain.should_receive(:new_entered)
37
37
  maintain.state = :new
38
- maintain.should_receive(:old_entered)
38
+ maintain.should_receive(:old_entered).once
39
39
  maintain.state = :old
40
- maintain.should_not_receive(:old_entered)
41
40
  maintain.state = :old
42
41
  end
43
42
 
data/spec/integer_spec.rb CHANGED
@@ -26,6 +26,7 @@ describe Maintain do
26
26
 
27
27
  it "should return valid names, too" do
28
28
  @maintainer.kind = :woman
29
+ @maintainer.kind.should == 2
29
30
  @maintainer.kind.name.should == "woman"
30
31
  end
31
32
  end
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: maintain
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 21
4
5
  prerelease: false
5
6
  segments:
6
7
  - 0
8
+ - 2
7
9
  - 1
8
- - 9
9
- version: 0.1.9
10
+ version: 0.2.1
10
11
  platform: ruby
11
12
  authors:
12
13
  - Flip Sasser
@@ -14,7 +15,7 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-05-06 00:00:00 -04:00
18
+ date: 2011-02-03 00:00:00 -05:00
18
19
  default_executable:
19
20
  dependencies: []
20
21
 
@@ -27,11 +28,15 @@ extensions: []
27
28
  extra_rdoc_files:
28
29
  - README.markdown
29
30
  files:
30
- - .gitignore
31
+ - .rspec
31
32
  - README.markdown
32
33
  - Rakefile
33
34
  - VERSION
35
+ - autotest/discover.rb
34
36
  - lib/maintain.rb
37
+ - lib/maintain/backend.rb
38
+ - lib/maintain/backend/active_record.rb
39
+ - lib/maintain/backend/base.rb
35
40
  - lib/maintain/bitmask_value.rb
36
41
  - lib/maintain/integer_value.rb
37
42
  - lib/maintain/maintainer.rb
@@ -49,33 +54,38 @@ files:
49
54
  - spec/proxy_spec.rb
50
55
  - spec/setting_state_spec.rb
51
56
  - spec/spec.opts
57
+ - spec/data_mapper_spec.rb
52
58
  has_rdoc: true
53
59
  homepage: http://github.com/flipsasser/maintain
54
60
  licenses: []
55
61
 
56
62
  post_install_message:
57
- rdoc_options:
58
- - --charset=UTF-8
63
+ rdoc_options: []
64
+
59
65
  require_paths:
60
66
  - lib
61
67
  required_ruby_version: !ruby/object:Gem::Requirement
68
+ none: false
62
69
  requirements:
63
70
  - - ">="
64
71
  - !ruby/object:Gem::Version
72
+ hash: 3
65
73
  segments:
66
74
  - 0
67
75
  version: "0"
68
76
  required_rubygems_version: !ruby/object:Gem::Requirement
77
+ none: false
69
78
  requirements:
70
79
  - - ">="
71
80
  - !ruby/object:Gem::Version
81
+ hash: 3
72
82
  segments:
73
83
  - 0
74
84
  version: "0"
75
85
  requirements: []
76
86
 
77
87
  rubyforge_project:
78
- rubygems_version: 1.3.6
88
+ rubygems_version: 1.3.7
79
89
  signing_key:
80
90
  specification_version: 3
81
91
  summary: A Ruby state machine that lets your code do the driving
@@ -84,6 +94,7 @@ test_files:
84
94
  - spec/aggregates_spec.rb
85
95
  - spec/bitwise_spec.rb
86
96
  - spec/comparing_state_spec.rb
97
+ - spec/data_mapper_spec.rb
87
98
  - spec/defining_states_spec.rb
88
99
  - spec/hooks_spec.rb
89
100
  - spec/integer_spec.rb
data/.gitignore DELETED
@@ -1,2 +0,0 @@
1
- coverage/
2
- *.gem