Class: Hobo::Model::Lifecycles::Lifecycle

Inherits:
Object
  • Object
show all
Defined in:
lib/hobo/model/lifecycles/lifecycle.rb

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(record) ⇒ Lifecycle

Returns a new instance of Lifecycle.



105
106
107
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 105

def initialize(record)
  @record = record
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args) ⇒ Object



231
232
233
234
235
236
237
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 231

def method_missing(name, *args)
  if name.to_s =~ /^(.*)_in_progress\?$/
    active_step_is?($1)
  else
    super
  end
end

Class Attribute Details

.creatorsObject

Returns the value of attribute creators.



21
22
23
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 21

def creators
  @creators
end

.default_stateObject

Returns the value of attribute default_state.



21
22
23
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 21

def default_state
  @default_state
end

.invariantsObject

Returns the value of attribute invariants.



21
22
23
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 21

def invariants
  @invariants
end

.modelObject

Returns the value of attribute model.



21
22
23
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 21

def model
  @model
end

.optionsObject

Returns the value of attribute options.



21
22
23
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 21

def options
  @options
end

.statesObject

Returns the value of attribute states.



21
22
23
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 21

def states
  @states
end

.transitionsObject

Returns the value of attribute transitions.



21
22
23
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 21

def transitions
  @transitions
end

Instance Attribute Details

#active_stepObject

Returns the value of attribute active_step.



102
103
104
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 102

def active_step
  @active_step
end

#provided_keyObject

Returns the value of attribute provided_key.



102
103
104
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 102

def provided_key
  @provided_key
end

#recordObject (readonly)

— Instance Features — #



100
101
102
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 100

def record
  @record
end

Class Method Details

.can_create?(name, user) ⇒ Boolean

Returns:



78
79
80
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 78

def self.can_create?(name, user)
  creators[name.to_sym].allowed?(user)
end

.create(name, user, attributes = nil) ⇒ Object



83
84
85
86
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 83

def self.create(name, user, attributes=nil)
  creator = creators[name.to_sym] or raise LifecycleError, "No creator #{name} available"
  creator.run!(user, attributes)
end

.creator(name) ⇒ Object



73
74
75
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 73

def self.creator(name)
  creators[name.to_sym] or raise ArgumentError, "No such creator in lifecycle: #{name}"
end

.def_creator(name, on_create, options) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 32

def self.def_creator(name, on_create, options)
  class_eval %{
               def self.#{name}(user, attributes=nil)
                 create(:#{name}, user, attributes)
               end
               def self.can_#{name}?(user, attributes=nil)
                 can_create?(:#{name}, user)
               end
              }
  Creator.new(self, name.to_s, on_create, options)
end

.def_state(name, on_enter) ⇒ Object



25
26
27
28
29
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 25

def self.def_state(name, on_enter)
  name = name.to_sym
  class_eval "def #{name}_state?; state_name == :#{name} end"
  states[name] = Lifecycles::State.new(name, on_enter)
end

.def_transition(name, start_states, end_state, on_transition, options) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 44

def self.def_transition(name, start_states, end_state, on_transition, options)
  class_eval %{
               def #{name}!(user, attributes=nil)
                 transition(:#{name}, user, attributes)
               end
               def can_#{name}?(user, attributes=nil)
                 can_transition?(:#{name}, user)
               end
              }
  Transition.new(self, name.to_s, start_states, end_state, on_transition, options)
end

.init(model, options) ⇒ Object



7
8
9
10
11
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 7

def self.init(model, options)
  @model   = model
  @options = options
  reset
end

.key_timeoutObject



93
94
95
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 93

def self.key_timeout
  options[:key_timeout]
end

.publishable_creatorsObject



60
61
62
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 60

def self.publishable_creators
  creators.values.where.publishable?
end

.publishable_transitionsObject



64
65
66
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 64

def self.publishable_transitions
  transitions.where.publishable?
end

.resetObject



13
14
15
16
17
18
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 13

def self.reset
  @states        = {}
  @creators      = {}
  @transitions   = []
  @invariants    = []
end

.state_fieldObject



89
90
91
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 89

def self.state_field
  options[:state_field]
end

.state_namesObject



56
57
58
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 56

def self.state_names
  states.keys
end

.step_namesObject



68
69
70
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 68

def self.step_names
  (creators.keys | transitions.*.name).uniq
end

Instance Method Details

#active_step_is?(name) ⇒ Boolean

Returns:



227
228
229
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 227

def active_step_is?(name)
  active_step && active_step.name == name.to_sym
end

#available_transitionsObject



137
138
139
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 137

def available_transitions
  state ? state.transitions_out : []
end

#available_transitions_for(user, name = nil) ⇒ Object

see also publishable_transitions_for



142
143
144
145
146
147
148
149
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 142

def available_transitions_for(user, name=nil)
  name = name.to_sym if name
  matches = available_transitions
  matches = matches.select { |t| t.name == name } if name
  record.with_acting_user(user) do
    matches.select { |t| t.can_run?(record) }
  end
end

#become(state_name, validate = true) ⇒ Object



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 160

def become(state_name, validate=true)
  state_name = state_name.to_sym
  record.send :write_attribute, self.class.state_field, state_name.to_s

  if state_name == :destroy
    record.destroy
    true
  else
    s = self.class.states[state_name]
    raise ArgumentError, "No such state '#{state_name}' for #{record.class.name}" unless s

    if record.save(:validate => validate)
      s.activate! record
      self.active_step = nil # That's the end of this step
      true
    else
      false
    end
  end
end

#can_transition?(name, user) ⇒ Boolean

Returns:



110
111
112
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 110

def can_transition?(name, user)
  available_transitions_for(user, name).any?
end

#clear_keyObject



218
219
220
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 218

def clear_key
  record.send :write_attribute, key_timestamp_field, nil
end

#find_transition(name, user) ⇒ Object



121
122
123
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 121

def find_transition(name, user)
  available_transitions_for(user, name).first
end

#generate_keyObject



190
191
192
193
194
195
196
197
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 190

def generate_key
  if Time.zone.nil?
    raise RuntimeError, "Cannot generate lifecycle key timestamp if the time-zone is not configured. Please add, e.g. config.time_zone = 'UTC' to environment.rb"
  end
  key_timestamp = Time.now.utc
  record.send :write_attribute, key_timestamp_field, key_timestamp
  key
end

#invariants_satisfied?Boolean

Returns:



222
223
224
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 222

def invariants_satisfied?
  self.class.invariants.all? { |i| record.instance_eval(&i) }
end

#keyObject



200
201
202
203
204
205
206
207
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 200

def key
  require 'digest/sha1'
  timestamp = record.read_attribute(key_timestamp_field)
  if timestamp
    timestamp = timestamp.getutc
    Digest::SHA1.hexdigest("#{record.id}-#{state_name}-#{timestamp}-#{Rails.application.config.secret_token}")
  end
end

#key_expired?Boolean

Returns:



209
210
211
212
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 209

def key_expired?
  timestamp = record.read_attribute(key_timestamp_field)
  timestamp.nil? || (timestamp.getutc + key_timeout < Time.now.utc)
end

#key_timeoutObject



186
187
188
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 186

def key_timeout
  record.class::Lifecycle.options[:key_timeout]
end

#key_timestamp_fieldObject



182
183
184
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 182

def key_timestamp_field
  record.class::Lifecycle.options[:key_timestamp_field]
end

#publishable_transitions_for(user) ⇒ Object



151
152
153
154
155
156
157
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 151

def publishable_transitions_for(user)
  record.with_acting_user(user) do
    available_transitions_for(user).select do |t|
      t.publishable_by(user, t.available_to, record)
    end
  end
end

#stateObject



132
133
134
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 132

def state
  self.class.states[state_name]
end

#state_nameObject



126
127
128
129
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 126

def state_name
  name = record.read_attribute(self.class.state_field)
  name.to_sym if name
end

#transition(name, user, attributes) ⇒ Object



115
116
117
118
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 115

def transition(name, user, attributes)
  transition = find_transition(name, user) or raise LifecycleError, "No transition #{name} available"
  transition.run!(record, user, attributes)
end

#valid_key?Boolean

Returns:



214
215
216
# File 'lib/hobo/model/lifecycles/lifecycle.rb', line 214

def valid_key?
  provided_key && provided_key == key && !key_expired?
end