state_attr 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # About
2
+
3
+ StateAttr is an minimalistic state machine approach for rails allowing multiple state attributes at the same time.
4
+
5
+ # Installation
6
+
7
+ gem install state_attr
8
+
9
+ # Examples
10
+
11
+ state_attr :state, {
12
+ nil => :first,
13
+ :first => [:second, :third],
14
+ :second => :last,
15
+ :third => nil,
16
+ }
17
+
18
+ state_attr :invitation_state, {
19
+ :invited => %w{approved rejected},
20
+ :approved => :rejected,
21
+ :rejected => :approved,
22
+ }, :initial => :invited
23
+
24
+ state_attr :progress, {
25
+ nil => :one,
26
+ :one => :two,
27
+ :two => :three
28
+ } do |old, new|
29
+ if new == :three
30
+ send_mail "You just finished."
31
+ else
32
+ send_mail "You just made to #{new}."
33
+ end
34
+ end
35
+
36
+ # Options
37
+
38
+ - :initial => :value - initial `value` (not yet implemented)
39
+ - :setter => :exception - raise exception when calling `state=`
40
+ - :switch_not_allowed => :silent - do not raise exception when switch is not allowed
data/Rakefile ADDED
@@ -0,0 +1,24 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the session_management plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.libs << 'test'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = true
14
+ end
15
+
16
+ desc 'Generate documentation for the session_management plugin.'
17
+ Rake::RDocTask.new(:rdoc) do |rdoc|
18
+ rdoc.rdoc_dir = 'rdoc'
19
+ rdoc.title = 'SessionManagement'
20
+ rdoc.options << '--line-numbers' << '--inline-source'
21
+ rdoc.rdoc_files.include('README.md')
22
+ rdoc.rdoc_files.include('lib/**/*.rb')
23
+ end
24
+
@@ -0,0 +1,72 @@
1
+ module StateAttr
2
+ module ClassMethods
3
+ class State
4
+
5
+ def initialize(parent, field, machine, logger, options)
6
+ @parent = parent
7
+ @field = field
8
+ #convert different types of input to array of symbols
9
+ @machine = {}
10
+ machine.each { |key, value| @machine[key] = value.nil? ? [nil] : Array(value).map(&:to_sym) }
11
+ @logger = logger
12
+ @options = options
13
+ @callback = "on_#{@field}_change".to_sym
14
+ end
15
+
16
+ def to_s
17
+ read_state.to_s
18
+ end
19
+
20
+ # current state value
21
+ def value
22
+ read_state
23
+ end
24
+ alias_method :current, :value
25
+
26
+ # validates if state is one of the given states
27
+ def is?(*symbols)
28
+ !symbols.flatten.select { |state| read_state == state }.empty?
29
+ end
30
+
31
+ # array of allowed stated
32
+ def allowed
33
+ @machine[read_state] || []
34
+ end
35
+
36
+ #validate if can switch to given state
37
+ def allowed?(state)
38
+ allowed.include?(state) || is?(state)
39
+ end
40
+
41
+ # if allowed switch to given state, if not raise exception
42
+ def switch(state)
43
+ if (allowed?(state))
44
+ write_state(state)
45
+ elsif @options[:switch_not_allowed] == :silent
46
+ @logger.error "#{@parent.class.name} changing #{@field} from '#{read_state}' to '#{state}' is not allowed, allowed states are '#{allowed*'\', \''}'"
47
+ false
48
+ else
49
+ raise "#{@parent.class.name} error, changing #{@field} from '#{read_state}' to '#{state}' is not allowed, allowed states are '#{allowed*'\', \''}'"
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def read_state
56
+ @parent.read_attribute(@field).try(:to_sym)
57
+ end
58
+
59
+ def write_state(state)
60
+ if @parent.methods.include? @callback.to_s
61
+ unless @parent.send @callback, read_state, state
62
+ @logger.warn "#{@parent.class.name}: change #{@field} from '#{read_state}' to '#{state}' was rejected by callback"
63
+ return false
64
+ end
65
+ end
66
+ @logger.debug "#{@parent.class.name}: changing #{@field} from '#{read_state}' to '#{state}'"
67
+ @parent.write_attribute(@field, state.try(:to_s))
68
+ true
69
+ end
70
+ end
71
+ end
72
+ end
data/lib/state_attr.rb ADDED
@@ -0,0 +1,36 @@
1
+ require 'state_attr/state'
2
+ module StateAttr
3
+
4
+ module ClassMethods
5
+ def state_attr(attr, machine, options={}, &block)
6
+ #attr, initial = attr
7
+ self.send :define_method, attr do
8
+ @state_handlers ||= {}
9
+ @state_handlers[attr] ||= State.new(self, attr, machine, logger, options)
10
+ @state_handlers[attr]
11
+ end
12
+
13
+ if options[:setter] == :exception
14
+ self.send :define_method, "#{attr}=".to_sym do |state|
15
+ raise "#{self.class.name} error, manual setting of state is not allowed (new value '#{state}')"
16
+ end
17
+ else
18
+ self.send :define_method, "#{attr}=".to_sym do |state|
19
+ @state_handlers ||= {}
20
+ @state_handlers[attr] ||= State.new(self, attr, machine, logger, options)
21
+ @state_handlers[attr].switch(state)
22
+ end
23
+ end
24
+
25
+ if block_given?
26
+ self.send :define_method, "on_#{attr}_change".to_sym do |*args|
27
+ block.call *args
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ def self.included(base)
34
+ base.extend ClassMethods
35
+ end
36
+ end
data/rails/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'state_attr'
2
+ ActiveRecord::Base.send( :include, StateAttr )
@@ -0,0 +1,22 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "state_attr"
3
+ s.version = "0.1.9"
4
+ s.date = "2011-05-16"
5
+ s.summary = "Minimalistic state machine approach allowing multiple state attributes at the same time."
6
+ s.email = "mpapis@gmail.com"
7
+ s.homepage = "http://github.com/mpapis/state_attr/tree/master"
8
+ s.description = "Minimalistic state machine."
9
+ s.has_rdoc = false
10
+ s.authors = ["Michal Papis"]
11
+ s.files = [
12
+ "lib/state_attr.rb",
13
+ "lib/state_attr/state.rb",
14
+ "rails/init.rb",
15
+ "Rakefile",
16
+ "README.md",
17
+ "state_attr.gemspec",
18
+ "test/test_helper.rb",
19
+ "test/unit/state_attr_test.rb",
20
+ ]
21
+ #s.add_dependency("rails",["~>2","~>3"])
22
+ end
@@ -0,0 +1,46 @@
1
+ require 'test/unit'
2
+ require 'state_attr'
3
+
4
+ # fake rails
5
+ class << nil
6
+ def try *args
7
+ nil
8
+ end
9
+ end
10
+
11
+ class Object
12
+ def try *args
13
+ send *args
14
+ end
15
+ end
16
+
17
+ class Logger
18
+ def info msg
19
+ end
20
+ def warn msg
21
+ end
22
+ def error msg
23
+ end
24
+ def debug msg
25
+ end
26
+ end
27
+ RAILS_DEFAULT_LOGGER = Logger.new
28
+
29
+ module ActiveRecord
30
+ class Base
31
+ def write_attribute(name, value)
32
+ @attributes ||= {}
33
+ @attributes[name] = value
34
+ end
35
+ def read_attribute(name)
36
+ attributes[name]
37
+ end
38
+ def attributes
39
+ @attributes || {}
40
+ end
41
+ def logger
42
+ RAILS_DEFAULT_LOGGER
43
+ end
44
+ include StateAttr
45
+ end
46
+ end
@@ -0,0 +1,79 @@
1
+ require 'test_helper'
2
+
3
+ class FakeModel < ActiveRecord::Base
4
+ state_attr :state1, {
5
+ nil => :a,
6
+ :a => :b,
7
+ }, :setter => :exception
8
+
9
+ state_attr :state2, {
10
+ nil => [:d,:f],
11
+ :d => [:e,:f],
12
+ :f => [:d,:e],
13
+ :e => nil,
14
+ }, :setter => :exception
15
+
16
+ state_attr :state3, {
17
+ nil => :g,
18
+ :g => :h,
19
+ }
20
+ end
21
+
22
+ class StateAttrTest < Test::Unit::TestCase
23
+ def test_single_set_read
24
+ model = FakeModel.new
25
+ assert_equal nil, model.state1.value
26
+ assert_equal "", model.state1.to_s
27
+ model.state1.switch(:a)
28
+ assert_equal :a, model.state1.value
29
+ assert_equal "a", "#{model.state1}"
30
+ end
31
+ def test_single_set_fail
32
+ model = FakeModel.new
33
+ assert_raises RuntimeError do
34
+ model.state1.switch(:c)
35
+ end
36
+ assert_equal nil, model.state1.value
37
+ end
38
+ def test_single_assignment_fail
39
+ model = FakeModel.new
40
+ assert_raises RuntimeError do
41
+ model.state1=:a
42
+ end
43
+ assert_equal nil, model.state1.value
44
+ end
45
+ def test_single_assignment_success
46
+ model = FakeModel.new
47
+ model.state3=:g
48
+ assert_equal :g, model.state3.value
49
+ end
50
+ def test_single_set_nil
51
+ model = FakeModel.new
52
+ model.state2.switch(:d)
53
+ model.state2.switch(:e)
54
+ assert_equal :e, model.state2.value
55
+ model.state2.switch(nil)
56
+ assert_equal nil, model.state2.value
57
+ end
58
+ def test_double_set
59
+ model = FakeModel.new
60
+ model.state1.switch(:a)
61
+ model.state2.switch(:d)
62
+ assert_equal :a, model.state1.value
63
+ assert_equal :d, model.state2.value
64
+ end
65
+ def test_single_is?
66
+ model = FakeModel.new
67
+ model.state2.switch(:d)
68
+ assert model.state2.is?(:d)
69
+ assert !model.state2.is?(:e)
70
+ assert model.state2.is?(:d,:e)
71
+ end
72
+ def test_single_allowed?
73
+ model = FakeModel.new
74
+ model.state2.switch(:d)
75
+ assert !model.state2.allowed?(nil)
76
+ assert model.state2.allowed?(:d)
77
+ assert model.state2.allowed?(:e)
78
+ end
79
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: state_attr
3
+ version: !ruby/object:Gem::Version
4
+ hash: 9
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 9
10
+ version: 0.1.9
11
+ platform: ruby
12
+ authors:
13
+ - Michal Papis
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-05-16 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Minimalistic state machine.
23
+ email: mpapis@gmail.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - lib/state_attr.rb
32
+ - lib/state_attr/state.rb
33
+ - rails/init.rb
34
+ - Rakefile
35
+ - README.md
36
+ - state_attr.gemspec
37
+ - test/test_helper.rb
38
+ - test/unit/state_attr_test.rb
39
+ has_rdoc: true
40
+ homepage: http://github.com/mpapis/state_attr/tree/master
41
+ licenses: []
42
+
43
+ post_install_message:
44
+ rdoc_options: []
45
+
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ hash: 3
54
+ segments:
55
+ - 0
56
+ version: "0"
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ hash: 3
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ requirements: []
67
+
68
+ rubyforge_project:
69
+ rubygems_version: 1.5.2
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: Minimalistic state machine approach allowing multiple state attributes at the same time.
73
+ test_files: []
74
+