self-control 0.0.0 → 0.1.0

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.
@@ -1,6 +1,10 @@
1
1
  = self-control
2
2
 
3
- Description goes here.
3
+ State machines are awesome but sometimes you need a little more. Like who should do what in order for it to move on? How many steps are left? How can I restfully trigger the next event? Self Control adds route helpers, controller actions and a dsl to turn your existing state machines into full workflows. It is designed to use rails 3 with ActiveModel and should work with any state machine with just a few extra methods.
4
+
5
+ == ToDo
6
+ * Documentation
7
+ * Tests
4
8
 
5
9
  == Note on Patches/Pull Requests
6
10
 
@@ -15,4 +19,4 @@ Description goes here.
15
19
 
16
20
  == Copyright
17
21
 
18
- Copyright (c) 2009 hexorx. See LICENSE for details.
22
+ Copyright (c) 2011 hexorx. See LICENSE for details.
data/Rakefile CHANGED
@@ -5,8 +5,8 @@ begin
5
5
  require 'jeweler'
6
6
  Jeweler::Tasks.new do |gem|
7
7
  gem.name = "self-control"
8
- gem.summary = %Q{Sinatra extention to easily add RESTful routes for your models.}
9
- gem.description = %Q{Self-Control provides default REST routes for your models.}
8
+ gem.summary = %Q{Self-Control builds on you existing state machine to create full work flows.}
9
+ gem.description = %Q{State machines are awesome but sometimes you need a little more. Like who should do what in order for it to move on? How many steps are left? How can I restfully trigger the next event? Self Control adds route helpers, controller actions and a dsl to turn your existing state machines into full workflows. It is designed to use rails 3 with ActiveModel and should work with any state machine with just a few extra methods.}
10
10
  gem.email = "hexorx@gmail.com"
11
11
  gem.homepage = "http://github.com/hexorx/self-control"
12
12
  gem.authors = ["hexorx"]
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.0
1
+ 0.1.0
@@ -0,0 +1,50 @@
1
+ require 'active_support'
2
+ require 'self-control/exceptions'
3
+
4
+ module SelfControl
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ attr_reader :selfcontrol_list
9
+ attr_writer :selfcontrol_column
10
+
11
+ def selfcontrol(state=nil, options={}, &block)
12
+ state = (state || options[:when]).to_sym
13
+ @selfcontrol_list ||= {}
14
+ @selfcontrol_list[state] = SelfControl::FlowBuilder.new(state, &block)
15
+ end
16
+
17
+ alias :control_when :selfcontrol
18
+
19
+ def selfcontrol_column
20
+ @selfcontrol_column ||= 'state'
21
+ end
22
+ end
23
+
24
+ module InstanceMethods
25
+ def selfcontrol_list
26
+ self.class.selfcontrol_list
27
+ end
28
+
29
+ def selfcontrol_column
30
+ self.class.selfcontrol_column
31
+ end
32
+
33
+ def current_selfcontrol_state
34
+ (self.respond_to?(selfcontrol_column) ? self.send(selfcontrol_column) : :default).to_sym
35
+ end
36
+
37
+ def selfcontrol(state=current_selfcontrol_state)
38
+ return unless builder = self.selfcontrol_list[state]
39
+ @selfcontrol_flows ||= {}
40
+ @selfcontrol_flows[state] ||= SelfControl::Flow.new(builder,self)
41
+ end
42
+ end
43
+
44
+ autoload :FlowBuilder, 'self-control/flow_builder'
45
+ autoload :StepBuilder, 'self-control/step_builder'
46
+ autoload :Flow, 'self-control/flow'
47
+ autoload :Step, 'self-control/step'
48
+ end
49
+
50
+ require 'self-control/railtie' if defined?(Rails)
@@ -0,0 +1,82 @@
1
+ module SelfControl
2
+ module ControllerAdditions
3
+ extend ActiveSupport::Concern
4
+
5
+ module InstanceMethods
6
+ def steps_list
7
+ @selfcontrol_steps = valid_selfcontrol? ? selfcontrol.steps_for(selfcontrol_actor) : []
8
+ respond_with(@selfcontrol_steps)
9
+ end
10
+
11
+ def start_step
12
+ authorize_selfcontrol_step!
13
+
14
+ if action_methods.include?(params[:id])
15
+ send(params[:id])
16
+ else
17
+ render params[:id]
18
+ end
19
+ end
20
+
21
+ def do_step
22
+ authorize_selfcontrol_step!
23
+ selfcontrol.do!(params[:id],params[:choose],params[:self_control_step])
24
+ respond_with(selfcontrol_resource, :location => selfcontrol_resource_location)
25
+ end
26
+
27
+ protected
28
+
29
+ def default_selfcontrol_actor
30
+ respond_to?(:current_user) ? send(:current_user) : nil
31
+ end
32
+
33
+ def selfcontrol_actor
34
+ @selfcontrol_actor ||= default_selfcontrol_actor
35
+ end
36
+
37
+ def default_selfcontrol_resource
38
+ instance_name = params[:controller].sub("Controller", "").underscore.split('/').last.singularize
39
+ instance_variable_get("@#{instance_name}")
40
+ end
41
+
42
+ def selfcontrol_resource
43
+ @selfcontrol_resource ||= default_selfcontrol_resource
44
+ end
45
+
46
+ def selfcontrol_resource_location
47
+ selfcontrol_resource
48
+ end
49
+
50
+ def selfcontrol
51
+ @selfcontrol ||= valid_selfcontrol? ? selfcontrol_resource.selfcontrol : nil
52
+ end
53
+
54
+ def selfcontrol_step
55
+ @selfcontrol_step ||= selfcontrol.nil? ? nil : selfcontrol[params[:id].to_sym]
56
+ end
57
+
58
+ def valid_selfcontrol?
59
+ !selfcontrol_resource.nil? &&
60
+ selfcontrol_resource.respond_to?(:selfcontrol) &&
61
+ selfcontrol_resource.selfcontrol.is_a?(SelfControl::Flow)
62
+ end
63
+
64
+ def valid_selfcontrol_step?
65
+ !!selfcontrol_step
66
+ end
67
+
68
+ def allow_selfcontrol_step?
69
+ return false unless selfcontrol.is_a?(SelfControl::Flow)
70
+ selfcontrol.allow? selfcontrol_actor, :to => params[:id]
71
+ end
72
+
73
+ def authorize_selfcontrol_step!
74
+ raise SelfControl::NotImplemented.new() unless valid_selfcontrol_step?
75
+ unless allow_selfcontrol_step?
76
+ raise SelfControl::AccessDenied.new(nil, selfcontrol_actor, params[:step], params[:choose])
77
+ end
78
+ end
79
+
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,22 @@
1
+ module SelfControl
2
+ class Error < StandardError; end
3
+
4
+ class NotImplemented < Error; end
5
+
6
+ class AccessDenied < Error
7
+ attr_reader :subject, :step, :action
8
+ attr_writer :default_message
9
+
10
+ def initialize(message=nil, subject=nil, step=nil, action=nil)
11
+ @message = message
12
+ @subject = subject
13
+ @step = step
14
+ @action = action
15
+ @default_message = "You are not authorized to access this step."
16
+ end
17
+
18
+ def to_s
19
+ @message || @default_message
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,70 @@
1
+ module SelfControl
2
+ class Flow
3
+ attr_reader :builder, :model
4
+
5
+ delegate :actions, :total, :total_doable, :to => :@builder
6
+
7
+ def initialize(builder, model)
8
+ @builder = builder
9
+ @model = model
10
+ @step_map = {}
11
+ end
12
+
13
+ def [](name)
14
+ return if name.nil?
15
+ name = name.to_sym
16
+ @step_map[name] ||= steps.detect { |step| step.name == name }
17
+ end
18
+
19
+ def steps
20
+ @steps ||= @builder.steps.map { |step| SelfControl::Step.new(step, self) }
21
+ end
22
+
23
+ def steps_for(actor)
24
+ steps.select { |step| step.allow?(actor) }
25
+ end
26
+
27
+ def next_steps_for(actor)
28
+ steps.detect { |step| step.doable? && !step.done? && step.allow?(actor) }
29
+ end
30
+
31
+ def completed_steps
32
+ steps.select(&:done?)
33
+ end
34
+
35
+ def total_completed
36
+ completed_steps.size
37
+ end
38
+
39
+ def progress(options={})
40
+ count = (options[:of] || :all).to_sym == :doable ? total_doable : total
41
+
42
+ case (options[:as] || :percent).to_sym
43
+ when :percent
44
+ count == 0 ? 100 : (total_completed / count.to_f) * 100
45
+ when :decimal
46
+ count == 0 ? 1.0 : (total_completed / count)
47
+ when :array
48
+ [total_completed, count]
49
+ when :string
50
+ [total_completed, count].join(options[:with] || ' / ')
51
+ end
52
+ end
53
+
54
+ def done?
55
+ total_completed == total_doable
56
+ end
57
+
58
+ def allow?(actor=nil,options={})
59
+ return false unless step = self[options[:to]]
60
+ step.allow?(actor)
61
+ end
62
+
63
+ def do!(*args)
64
+ options = args.extract_options!
65
+ return false unless step = self[args[0]]
66
+ step.do!(args[1],options)
67
+ end
68
+ end
69
+ end
70
+
@@ -0,0 +1,47 @@
1
+ module SelfControl
2
+ class FlowBuilder
3
+ attr_accessor :state, :steps, :actors, :actions
4
+
5
+ def initialize(state, &flow)
6
+ @state = state
7
+ @steps ||= []
8
+ @actors ||= []
9
+ @actions ||= []
10
+
11
+ instance_eval(&flow)
12
+
13
+ @actors = @actors.flatten.compact.uniq
14
+ @actions = @actions.flatten.compact.uniq
15
+ end
16
+
17
+ def total
18
+ @steps.size
19
+ end
20
+
21
+ def doable_steps
22
+ @steps.select(&:doable?)
23
+ end
24
+
25
+ def total_doable
26
+ doable_steps.size
27
+ end
28
+
29
+ private
30
+
31
+ def the(actor, options)
32
+ step = SelfControl::StepBuilder.new(actor, options)
33
+ @steps.push(step)
34
+ @actors.push(step.actor)
35
+ @actions.concat(step.actions)
36
+ end
37
+
38
+ def someone(options)
39
+ the(nil,options)
40
+ end
41
+
42
+ def they(options)
43
+ the(@actors.last,options)
44
+ end
45
+ end
46
+ end
47
+
@@ -0,0 +1,8 @@
1
+ require 'self-control/route_helpers'
2
+ require 'self-control/controller_additions'
3
+ require 'self-control/step_adapter'
4
+
5
+ module SelfControl
6
+ class Railtie < Rails::Railtie
7
+ end
8
+ end
@@ -0,0 +1,17 @@
1
+ module ActionDispatch::Routing
2
+ class Mapper
3
+ def selfcontrol
4
+ get 'steps', :action => 'steps_list', :as => 'self_control_steps_list'
5
+ get 'steps/:id', :action => 'start_step', :as => 'self_control_steps'
6
+ post 'steps/:id(/:choose)', :action => 'do_step', :as => 'self_control_steps'
7
+ end
8
+
9
+ def control_resources(*resources, &block)
10
+ resources(*resources) do
11
+ selfcontrol
12
+ yield if block_given?
13
+ end
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,60 @@
1
+ module SelfControl
2
+ class Step
3
+ delegate :name, :default, :actions, :doable?, :meta, :to => :@builder
4
+
5
+ attr_accessor :model
6
+
7
+ def initialize(builder, flow)
8
+ @builder = builder
9
+ @flow = flow
10
+ @model = flow.model
11
+ end
12
+
13
+ def done?
14
+ doable? && condition?(@builder.if || true) && !condition?(@builder.unless || false)
15
+ end
16
+
17
+ def condition?(attr)
18
+ return attr if attr.is_a?(Boolean)
19
+
20
+ result = if attr.is_a?(Proc)
21
+ attr.call(*[@model,@builder].take(attr.arity >= 0 ? attr.arity : 0))
22
+ elsif @model.respond_to?(attr)
23
+ @model.send(attr)
24
+ else
25
+ false
26
+ end
27
+
28
+ (result == 0 ? false : result.present?)
29
+ end
30
+
31
+ def actor_method
32
+ return if @builder.actor.nil?
33
+ @builder.actor.to_sym
34
+ end
35
+
36
+ def model_name
37
+ ActiveModel::Naming.singular(@model) if defined?(ActiveModel::Naming)
38
+ end
39
+
40
+ def actor
41
+ return if actor_method.nil?
42
+ @model.respond_to?(actor_method) ? @model.send(actor_method) : nil
43
+ end
44
+
45
+ def allow?(person=nil)
46
+ actor ? person == actor : true
47
+ end
48
+
49
+ def do!(*args)
50
+ options = args.extract_options!
51
+ action = options.delete(:choose) || args[0] || default
52
+ return false unless action && actions.include?(action.to_sym) && @model.respond_to?(action)
53
+
54
+ @model.attributes = options[model_name] if options[model_name]
55
+ @model.send(action)
56
+ @model.save
57
+ end
58
+ end
59
+ end
60
+
@@ -0,0 +1,25 @@
1
+ module SelfControl
2
+ class Step
3
+ include ActiveModel::Conversion
4
+ include ActiveModel::Validations
5
+ extend ActiveModel::Naming
6
+ extend ActiveModel::Translation
7
+
8
+ def persisted?
9
+ false
10
+ end
11
+
12
+ def as_json(options={})
13
+ {
14
+ :name => name,
15
+ :actor => actor_method,
16
+ :actions => actions,
17
+ :default => default,
18
+ :doable => doable?,
19
+ :done => done?,
20
+ :meta => meta
21
+ }
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ module SelfControl
2
+ class StepBuilder
3
+ attr_accessor :actor, :default, :actions, :name, :if, :unless, :meta
4
+
5
+ def initialize(actor, options)
6
+ @actor = actor
7
+ @actions = [*options.delete(:should)].flatten.compact.uniq.map(&:to_sym)
8
+ @default = options.delete(:action) || (@actions.size == 1 ? @actions.first : nil)
9
+ @name = (options.delete(:to) || @actions.join('_or_')).to_sym
10
+
11
+ @if = options.delete(:if)
12
+ @unless = options.delete(:unless)
13
+
14
+ @doable = !!(@if || @unless)
15
+
16
+ @meta = options
17
+ end
18
+
19
+ def doable?
20
+ @doable
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,59 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{self-control}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["hexorx"]
12
+ s.date = %q{2011-06-20}
13
+ s.description = %q{State machines are awesome but sometimes you need a little more. Like who should do what in order for it to move on? How many steps are left? How can I restfully trigger the next event? Self Control adds route helpers, controller actions and a dsl to turn your existing state machines into full workflows. It is designed to use rails 3 with ActiveModel and should work with any state machine with just a few extra methods.}
14
+ s.email = %q{hexorx@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "LICENSE",
22
+ "README.rdoc",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "lib/self-control.rb",
26
+ "lib/self-control/controller_additions.rb",
27
+ "lib/self-control/exceptions.rb",
28
+ "lib/self-control/flow.rb",
29
+ "lib/self-control/flow_builder.rb",
30
+ "lib/self-control/railtie.rb",
31
+ "lib/self-control/route_helpers.rb",
32
+ "lib/self-control/step.rb",
33
+ "lib/self-control/step_adapter.rb",
34
+ "lib/self-control/step_builder.rb",
35
+ "self-control.gemspec",
36
+ "spec/self-control_spec.rb",
37
+ "spec/spec_helper.rb"
38
+ ]
39
+ s.homepage = %q{http://github.com/hexorx/self-control}
40
+ s.require_paths = ["lib"]
41
+ s.rubygems_version = %q{1.6.2}
42
+ s.summary = %q{Self-Control builds on you existing state machine to create full work flows.}
43
+
44
+ if s.respond_to? :specification_version then
45
+ s.specification_version = 3
46
+
47
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
48
+ s.add_development_dependency(%q<rspec>, [">= 0"])
49
+ s.add_development_dependency(%q<yard>, [">= 0"])
50
+ else
51
+ s.add_dependency(%q<rspec>, [">= 0"])
52
+ s.add_dependency(%q<yard>, [">= 0"])
53
+ end
54
+ else
55
+ s.add_dependency(%q<rspec>, [">= 0"])
56
+ s.add_dependency(%q<yard>, [">= 0"])
57
+ end
58
+ end
59
+
metadata CHANGED
@@ -1,7 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: self-control
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
5
11
  platform: ruby
6
12
  authors:
7
13
  - hexorx
@@ -9,30 +15,38 @@ autorequire:
9
15
  bindir: bin
10
16
  cert_chain: []
11
17
 
12
- date: 2009-10-13 00:00:00 -06:00
18
+ date: 2011-06-20 00:00:00 -06:00
13
19
  default_executable:
14
20
  dependencies:
15
21
  - !ruby/object:Gem::Dependency
16
22
  name: rspec
17
- type: :development
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
20
26
  requirements:
21
27
  - - ">="
22
28
  - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
23
32
  version: "0"
24
- version:
33
+ type: :development
34
+ version_requirements: *id001
25
35
  - !ruby/object:Gem::Dependency
26
36
  name: yard
27
- type: :development
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
30
40
  requirements:
31
41
  - - ">="
32
42
  - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
33
46
  version: "0"
34
- version:
35
- description: Self-Control provides default REST routes for your models.
47
+ type: :development
48
+ version_requirements: *id002
49
+ description: State machines are awesome but sometimes you need a little more. Like who should do what in order for it to move on? How many steps are left? How can I restfully trigger the next event? Self Control adds route helpers, controller actions and a dsl to turn your existing state machines into full workflows. It is designed to use rails 3 with ActiveModel and should work with any state machine with just a few extra methods.
36
50
  email: hexorx@gmail.com
37
51
  executables: []
38
52
 
@@ -43,12 +57,21 @@ extra_rdoc_files:
43
57
  - README.rdoc
44
58
  files:
45
59
  - .document
46
- - .gitignore
47
60
  - LICENSE
48
61
  - README.rdoc
49
62
  - Rakefile
50
63
  - VERSION
51
64
  - lib/self-control.rb
65
+ - lib/self-control/controller_additions.rb
66
+ - lib/self-control/exceptions.rb
67
+ - lib/self-control/flow.rb
68
+ - lib/self-control/flow_builder.rb
69
+ - lib/self-control/railtie.rb
70
+ - lib/self-control/route_helpers.rb
71
+ - lib/self-control/step.rb
72
+ - lib/self-control/step_adapter.rb
73
+ - lib/self-control/step_builder.rb
74
+ - self-control.gemspec
52
75
  - spec/self-control_spec.rb
53
76
  - spec/spec_helper.rb
54
77
  has_rdoc: true
@@ -56,29 +79,34 @@ homepage: http://github.com/hexorx/self-control
56
79
  licenses: []
57
80
 
58
81
  post_install_message:
59
- rdoc_options:
60
- - --charset=UTF-8
82
+ rdoc_options: []
83
+
61
84
  require_paths:
62
85
  - lib
63
86
  required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
64
88
  requirements:
65
89
  - - ">="
66
90
  - !ruby/object:Gem::Version
91
+ hash: 3
92
+ segments:
93
+ - 0
67
94
  version: "0"
68
- version:
69
95
  required_rubygems_version: !ruby/object:Gem::Requirement
96
+ none: false
70
97
  requirements:
71
98
  - - ">="
72
99
  - !ruby/object:Gem::Version
100
+ hash: 3
101
+ segments:
102
+ - 0
73
103
  version: "0"
74
- version:
75
104
  requirements: []
76
105
 
77
106
  rubyforge_project:
78
- rubygems_version: 1.3.5
107
+ rubygems_version: 1.6.2
79
108
  signing_key:
80
109
  specification_version: 3
81
- summary: Sinatra extention to easily add RESTful routes for your models.
82
- test_files:
83
- - spec/self-control_spec.rb
84
- - spec/spec_helper.rb
110
+ summary: Self-Control builds on you existing state machine to create full work flows.
111
+ test_files: []
112
+
data/.gitignore DELETED
@@ -1,5 +0,0 @@
1
- *.sw?
2
- .DS_Store
3
- coverage
4
- rdoc
5
- pkg