cassandra_object 0.6.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. data/lib/cassandra_object/associations/one_to_many.rb +136 -0
  2. data/lib/cassandra_object/associations/one_to_one.rb +77 -0
  3. data/lib/cassandra_object/associations.rb +35 -0
  4. data/lib/cassandra_object/attributes.rb +93 -0
  5. data/lib/cassandra_object/base.rb +104 -0
  6. data/lib/cassandra_object/callbacks.rb +10 -0
  7. data/lib/cassandra_object/collection.rb +8 -0
  8. data/lib/cassandra_object/cursor.rb +86 -0
  9. data/lib/cassandra_object/dirty.rb +27 -0
  10. data/lib/cassandra_object/identity/abstract_key_factory.rb +36 -0
  11. data/lib/cassandra_object/identity/key.rb +20 -0
  12. data/lib/cassandra_object/identity/natural_key_factory.rb +51 -0
  13. data/lib/cassandra_object/identity/uuid_key_factory.rb +37 -0
  14. data/lib/cassandra_object/identity.rb +61 -0
  15. data/lib/cassandra_object/indexes.rb +129 -0
  16. data/lib/cassandra_object/legacy_callbacks.rb +33 -0
  17. data/lib/cassandra_object/migrations.rb +72 -0
  18. data/lib/cassandra_object/mocking.rb +15 -0
  19. data/lib/cassandra_object/persistence.rb +193 -0
  20. data/lib/cassandra_object/serialization.rb +6 -0
  21. data/lib/cassandra_object/type_registration.rb +7 -0
  22. data/lib/cassandra_object/types.rb +128 -0
  23. data/lib/cassandra_object/validation.rb +58 -0
  24. data/lib/cassandra_object.rb +30 -0
  25. data/vendor/active_support_shims.rb +4 -0
  26. data/vendor/activemodel/CHANGELOG +13 -0
  27. data/vendor/activemodel/CHANGES +12 -0
  28. data/vendor/activemodel/MIT-LICENSE +21 -0
  29. data/vendor/activemodel/README +21 -0
  30. data/vendor/activemodel/Rakefile +52 -0
  31. data/vendor/activemodel/activemodel.gemspec +19 -0
  32. data/vendor/activemodel/examples/validations.rb +29 -0
  33. data/vendor/activemodel/lib/active_model/attribute_methods.rb +291 -0
  34. data/vendor/activemodel/lib/active_model/callbacks.rb +91 -0
  35. data/vendor/activemodel/lib/active_model/conversion.rb +8 -0
  36. data/vendor/activemodel/lib/active_model/deprecated_error_methods.rb +33 -0
  37. data/vendor/activemodel/lib/active_model/dirty.rb +126 -0
  38. data/vendor/activemodel/lib/active_model/errors.rb +162 -0
  39. data/vendor/activemodel/lib/active_model/lint.rb +91 -0
  40. data/vendor/activemodel/lib/active_model/locale/en.yml +27 -0
  41. data/vendor/activemodel/lib/active_model/naming.rb +45 -0
  42. data/vendor/activemodel/lib/active_model/observing.rb +191 -0
  43. data/vendor/activemodel/lib/active_model/railtie.rb +2 -0
  44. data/vendor/activemodel/lib/active_model/serialization.rb +30 -0
  45. data/vendor/activemodel/lib/active_model/serializers/json.rb +96 -0
  46. data/vendor/activemodel/lib/active_model/serializers/xml.rb +204 -0
  47. data/vendor/activemodel/lib/active_model/state_machine/event.rb +62 -0
  48. data/vendor/activemodel/lib/active_model/state_machine/machine.rb +75 -0
  49. data/vendor/activemodel/lib/active_model/state_machine/state.rb +47 -0
  50. data/vendor/activemodel/lib/active_model/state_machine/state_transition.rb +40 -0
  51. data/vendor/activemodel/lib/active_model/state_machine.rb +70 -0
  52. data/vendor/activemodel/lib/active_model/test_case.rb +18 -0
  53. data/vendor/activemodel/lib/active_model/translation.rb +44 -0
  54. data/vendor/activemodel/lib/active_model/validations/acceptance.rb +55 -0
  55. data/vendor/activemodel/lib/active_model/validations/confirmation.rb +47 -0
  56. data/vendor/activemodel/lib/active_model/validations/exclusion.rb +42 -0
  57. data/vendor/activemodel/lib/active_model/validations/format.rb +64 -0
  58. data/vendor/activemodel/lib/active_model/validations/inclusion.rb +42 -0
  59. data/vendor/activemodel/lib/active_model/validations/length.rb +117 -0
  60. data/vendor/activemodel/lib/active_model/validations/numericality.rb +111 -0
  61. data/vendor/activemodel/lib/active_model/validations/presence.rb +42 -0
  62. data/vendor/activemodel/lib/active_model/validations/with.rb +59 -0
  63. data/vendor/activemodel/lib/active_model/validations.rb +120 -0
  64. data/vendor/activemodel/lib/active_model/validator.rb +110 -0
  65. data/vendor/activemodel/lib/active_model/version.rb +9 -0
  66. data/vendor/activemodel/lib/active_model.rb +61 -0
  67. data/vendor/activemodel/test/cases/attribute_methods_test.rb +46 -0
  68. data/vendor/activemodel/test/cases/callbacks_test.rb +70 -0
  69. data/vendor/activemodel/test/cases/helper.rb +23 -0
  70. data/vendor/activemodel/test/cases/lint_test.rb +28 -0
  71. data/vendor/activemodel/test/cases/naming_test.rb +28 -0
  72. data/vendor/activemodel/test/cases/observing_test.rb +133 -0
  73. data/vendor/activemodel/test/cases/serializeration/json_serialization_test.rb +83 -0
  74. data/vendor/activemodel/test/cases/serializeration/xml_serialization_test.rb +110 -0
  75. data/vendor/activemodel/test/cases/state_machine/event_test.rb +49 -0
  76. data/vendor/activemodel/test/cases/state_machine/machine_test.rb +43 -0
  77. data/vendor/activemodel/test/cases/state_machine/state_test.rb +72 -0
  78. data/vendor/activemodel/test/cases/state_machine/state_transition_test.rb +84 -0
  79. data/vendor/activemodel/test/cases/state_machine_test.rb +312 -0
  80. data/vendor/activemodel/test/cases/tests_database.rb +37 -0
  81. data/vendor/activemodel/test/cases/translation_test.rb +45 -0
  82. data/vendor/activemodel/test/cases/validations/acceptance_validation_test.rb +71 -0
  83. data/vendor/activemodel/test/cases/validations/conditional_validation_test.rb +141 -0
  84. data/vendor/activemodel/test/cases/validations/confirmation_validation_test.rb +58 -0
  85. data/vendor/activemodel/test/cases/validations/exclusion_validation_test.rb +47 -0
  86. data/vendor/activemodel/test/cases/validations/format_validation_test.rb +118 -0
  87. data/vendor/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb +175 -0
  88. data/vendor/activemodel/test/cases/validations/i18n_validation_test.rb +527 -0
  89. data/vendor/activemodel/test/cases/validations/inclusion_validation_test.rb +71 -0
  90. data/vendor/activemodel/test/cases/validations/length_validation_test.rb +437 -0
  91. data/vendor/activemodel/test/cases/validations/numericality_validation_test.rb +180 -0
  92. data/vendor/activemodel/test/cases/validations/presence_validation_test.rb +70 -0
  93. data/vendor/activemodel/test/cases/validations/with_validation_test.rb +166 -0
  94. data/vendor/activemodel/test/cases/validations_test.rb +215 -0
  95. data/vendor/activemodel/test/config.rb +3 -0
  96. data/vendor/activemodel/test/fixtures/topics.yml +41 -0
  97. data/vendor/activemodel/test/models/contact.rb +7 -0
  98. data/vendor/activemodel/test/models/custom_reader.rb +17 -0
  99. data/vendor/activemodel/test/models/developer.rb +6 -0
  100. data/vendor/activemodel/test/models/person.rb +9 -0
  101. data/vendor/activemodel/test/models/reply.rb +34 -0
  102. data/vendor/activemodel/test/models/topic.rb +9 -0
  103. data/vendor/activemodel/test/models/track_back.rb +4 -0
  104. data/vendor/activemodel/test/schema.rb +14 -0
  105. data/vendor/activesupport/lib/active_support/autoload.rb +48 -0
  106. data/vendor/activesupport/lib/active_support/concern.rb +25 -0
  107. data/vendor/activesupport/lib/active_support/core_ext/array/wrap.rb +20 -0
  108. data/vendor/activesupport/lib/active_support/core_ext/object/blank.rb +58 -0
  109. data/vendor/activesupport/lib/active_support/core_ext/object/tap.rb +6 -0
  110. data/vendor/activesupport/lib/active_support/dependency_module.rb +17 -0
  111. data/vendor/activesupport/lib/active_support/i18n.rb +2 -0
  112. data/vendor/activesupport/lib/active_support/locale/en.yml +33 -0
  113. metadata +230 -0
@@ -0,0 +1,30 @@
1
+ require 'active_support/core_ext/hash/except'
2
+ require 'active_support/core_ext/hash/slice'
3
+
4
+ module ActiveModel
5
+ module Serialization
6
+ def serializable_hash(options = nil)
7
+ options ||= {}
8
+
9
+ options[:only] = Array.wrap(options[:only]).map { |n| n.to_s }
10
+ options[:except] = Array.wrap(options[:except]).map { |n| n.to_s }
11
+
12
+ attribute_names = attributes.keys.sort
13
+ if options[:only].any?
14
+ attribute_names &= options[:only]
15
+ elsif options[:except].any?
16
+ attribute_names -= options[:except]
17
+ end
18
+
19
+ method_names = Array.wrap(options[:methods]).inject([]) do |methods, name|
20
+ methods << name if respond_to?(name.to_s)
21
+ methods
22
+ end
23
+
24
+ (attribute_names + method_names).inject({}) { |hash, name|
25
+ hash[name] = send(name)
26
+ hash
27
+ }
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,96 @@
1
+ require 'active_support/json'
2
+ require 'active_support/core_ext/class/attribute_accessors'
3
+
4
+ module ActiveModel
5
+ module Serializers
6
+ module JSON
7
+ extend ActiveSupport::Concern
8
+ include ActiveModel::Serialization
9
+
10
+ included do
11
+ extend ActiveModel::Naming
12
+
13
+ cattr_accessor :include_root_in_json, :instance_writer => true
14
+ end
15
+
16
+ # Returns a JSON string representing the model. Some configuration is
17
+ # available through +options+.
18
+ #
19
+ # The option <tt>ActiveModel::Base.include_root_in_json</tt> controls the
20
+ # top-level behavior of to_json. It is true by default. When it is <tt>true</tt>,
21
+ # to_json will emit a single root node named after the object's type. For example:
22
+ #
23
+ # konata = User.find(1)
24
+ # konata.to_json
25
+ # # => { "user": {"id": 1, "name": "Konata Izumi", "age": 16,
26
+ # "created_at": "2006/08/01", "awesome": true} }
27
+ #
28
+ # ActiveRecord::Base.include_root_in_json = false
29
+ # konata.to_json
30
+ # # => {"id": 1, "name": "Konata Izumi", "age": 16,
31
+ # "created_at": "2006/08/01", "awesome": true}
32
+ #
33
+ # The remainder of the examples in this section assume include_root_in_json is set to
34
+ # <tt>false</tt>.
35
+ #
36
+ # Without any +options+, the returned JSON string will include all
37
+ # the model's attributes. For example:
38
+ #
39
+ # konata = User.find(1)
40
+ # konata.to_json
41
+ # # => {"id": 1, "name": "Konata Izumi", "age": 16,
42
+ # "created_at": "2006/08/01", "awesome": true}
43
+ #
44
+ # The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the attributes
45
+ # included, and work similar to the +attributes+ method. For example:
46
+ #
47
+ # konata.to_json(:only => [ :id, :name ])
48
+ # # => {"id": 1, "name": "Konata Izumi"}
49
+ #
50
+ # konata.to_json(:except => [ :id, :created_at, :age ])
51
+ # # => {"name": "Konata Izumi", "awesome": true}
52
+ #
53
+ # To include any methods on the model, use <tt>:methods</tt>.
54
+ #
55
+ # konata.to_json(:methods => :permalink)
56
+ # # => {"id": 1, "name": "Konata Izumi", "age": 16,
57
+ # "created_at": "2006/08/01", "awesome": true,
58
+ # "permalink": "1-konata-izumi"}
59
+ #
60
+ # To include associations, use <tt>:include</tt>.
61
+ #
62
+ # konata.to_json(:include => :posts)
63
+ # # => {"id": 1, "name": "Konata Izumi", "age": 16,
64
+ # "created_at": "2006/08/01", "awesome": true,
65
+ # "posts": [{"id": 1, "author_id": 1, "title": "Welcome to the weblog"},
66
+ # {"id": 2, author_id: 1, "title": "So I was thinking"}]}
67
+ #
68
+ # 2nd level and higher order associations work as well:
69
+ #
70
+ # konata.to_json(:include => { :posts => {
71
+ # :include => { :comments => {
72
+ # :only => :body } },
73
+ # :only => :title } })
74
+ # # => {"id": 1, "name": "Konata Izumi", "age": 16,
75
+ # "created_at": "2006/08/01", "awesome": true,
76
+ # "posts": [{"comments": [{"body": "1st post!"}, {"body": "Second!"}],
77
+ # "title": "Welcome to the weblog"},
78
+ # {"comments": [{"body": "Don't think too hard"}],
79
+ # "title": "So I was thinking"}]}
80
+ def encode_json(encoder)
81
+ hash = serializable_hash(encoder.options)
82
+ hash = { self.class.model_name.element => hash } if include_root_in_json
83
+ ActiveSupport::JSON.encode(hash)
84
+ end
85
+
86
+ def as_json(options = nil)
87
+ self
88
+ end
89
+
90
+ def from_json(json)
91
+ self.attributes = ActiveSupport::JSON.decode(json)
92
+ self
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,204 @@
1
+ require 'active_support/core_ext/class/attribute_accessors'
2
+ require 'active_support/core_ext/hash/conversions'
3
+
4
+ module ActiveModel
5
+ module Serializers
6
+ module Xml
7
+ extend ActiveSupport::Concern
8
+ include ActiveModel::Serialization
9
+
10
+ class Serializer #:nodoc:
11
+ class Attribute #:nodoc:
12
+ attr_reader :name, :value, :type
13
+
14
+ def initialize(name, serializable)
15
+ @name, @serializable = name, serializable
16
+ @type = compute_type
17
+ @value = compute_value
18
+ end
19
+
20
+ # There is a significant speed improvement if the value
21
+ # does not need to be escaped, as <tt>tag!</tt> escapes all values
22
+ # to ensure that valid XML is generated. For known binary
23
+ # values, it is at least an order of magnitude faster to
24
+ # Base64 encode binary values and directly put them in the
25
+ # output XML than to pass the original value or the Base64
26
+ # encoded value to the <tt>tag!</tt> method. It definitely makes
27
+ # no sense to Base64 encode the value and then give it to
28
+ # <tt>tag!</tt>, since that just adds additional overhead.
29
+ def needs_encoding?
30
+ ![ :binary, :date, :datetime, :boolean, :float, :integer ].include?(type)
31
+ end
32
+
33
+ def decorations(include_types = true)
34
+ decorations = {}
35
+
36
+ if type == :binary
37
+ decorations[:encoding] = 'base64'
38
+ end
39
+
40
+ if include_types && type != :string
41
+ decorations[:type] = type
42
+ end
43
+
44
+ if value.nil?
45
+ decorations[:nil] = true
46
+ end
47
+
48
+ decorations
49
+ end
50
+
51
+ protected
52
+ def compute_type
53
+ value = @serializable.send(name)
54
+ type = Hash::XML_TYPE_NAMES[value.class.name]
55
+ type ||= :string if value.respond_to?(:to_str)
56
+ type ||= :yaml
57
+ type
58
+ end
59
+
60
+ def compute_value
61
+ value = @serializable.send(name)
62
+
63
+ if formatter = Hash::XML_FORMATTING[type.to_s]
64
+ value ? formatter.call(value) : nil
65
+ else
66
+ value
67
+ end
68
+ end
69
+ end
70
+
71
+ class MethodAttribute < Attribute #:nodoc:
72
+ protected
73
+ def compute_type
74
+ Hash::XML_TYPE_NAMES[@serializable.send(name).class.name] || :string
75
+ end
76
+ end
77
+
78
+ attr_reader :options
79
+
80
+ def initialize(serializable, options = nil)
81
+ @serializable = serializable
82
+ @options = options ? options.dup : {}
83
+
84
+ @options[:only] = Array.wrap(@options[:only]).map { |n| n.to_s }
85
+ @options[:except] = Array.wrap(@options[:except]).map { |n| n.to_s }
86
+ end
87
+
88
+ # To replicate the behavior in ActiveRecord#attributes,
89
+ # <tt>:except</tt> takes precedence over <tt>:only</tt>. If <tt>:only</tt> is not set
90
+ # for a N level model but is set for the N+1 level models,
91
+ # then because <tt>:except</tt> is set to a default value, the second
92
+ # level model can have both <tt>:except</tt> and <tt>:only</tt> set. So if
93
+ # <tt>:only</tt> is set, always delete <tt>:except</tt>.
94
+ def serializable_attribute_names
95
+ attribute_names = @serializable.attributes.keys.sort
96
+
97
+ if options[:only].any?
98
+ attribute_names &= options[:only]
99
+ elsif options[:except].any?
100
+ attribute_names -= options[:except]
101
+ end
102
+
103
+ attribute_names
104
+ end
105
+
106
+ def serializable_attributes
107
+ serializable_attribute_names.collect { |name| Attribute.new(name, @serializable) }
108
+ end
109
+
110
+ def serializable_method_attributes
111
+ Array(options[:methods]).inject([]) do |methods, name|
112
+ methods << MethodAttribute.new(name.to_s, @serializable) if @serializable.respond_to?(name.to_s)
113
+ methods
114
+ end
115
+ end
116
+
117
+ def serialize
118
+ args = [root]
119
+
120
+ if options[:namespace]
121
+ args << {:xmlns => options[:namespace]}
122
+ end
123
+
124
+ if options[:type]
125
+ args << {:type => options[:type]}
126
+ end
127
+
128
+ builder.tag!(*args) do
129
+ add_attributes
130
+ procs = options.delete(:procs)
131
+ options[:procs] = procs
132
+ add_procs
133
+ yield builder if block_given?
134
+ end
135
+ end
136
+
137
+ private
138
+ def builder
139
+ @builder ||= begin
140
+ require 'builder' unless defined? ::Builder
141
+ options[:indent] ||= 2
142
+ builder = options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
143
+
144
+ unless options[:skip_instruct]
145
+ builder.instruct!
146
+ options[:skip_instruct] = true
147
+ end
148
+
149
+ builder
150
+ end
151
+ end
152
+
153
+ def root
154
+ root = (options[:root] || @serializable.class.model_name.singular).to_s
155
+ reformat_name(root)
156
+ end
157
+
158
+ def dasherize?
159
+ !options.has_key?(:dasherize) || options[:dasherize]
160
+ end
161
+
162
+ def camelize?
163
+ options.has_key?(:camelize) && options[:camelize]
164
+ end
165
+
166
+ def reformat_name(name)
167
+ name = name.camelize if camelize?
168
+ dasherize? ? name.dasherize : name
169
+ end
170
+
171
+ def add_attributes
172
+ (serializable_attributes + serializable_method_attributes).each do |attribute|
173
+ builder.tag!(
174
+ reformat_name(attribute.name),
175
+ attribute.value.to_s,
176
+ attribute.decorations(!options[:skip_types])
177
+ )
178
+ end
179
+ end
180
+
181
+ def add_procs
182
+ if procs = options.delete(:procs)
183
+ [ *procs ].each do |proc|
184
+ if proc.arity > 1
185
+ proc.call(options, @serializable)
186
+ else
187
+ proc.call(options)
188
+ end
189
+ end
190
+ end
191
+ end
192
+ end
193
+
194
+ def to_xml(options = {}, &block)
195
+ Serializer.new(self, options).serialize(&block)
196
+ end
197
+
198
+ def from_xml(xml)
199
+ self.attributes = Hash.from_xml(xml).values.first
200
+ self
201
+ end
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,62 @@
1
+ module ActiveModel
2
+ module StateMachine
3
+ class Event
4
+ attr_reader :name, :success
5
+
6
+ def initialize(machine, name, options = {}, &block)
7
+ @machine, @name, @transitions = machine, name, []
8
+ if machine
9
+ machine.klass.send(:define_method, "#{name}!") do |*args|
10
+ machine.fire_event(name, self, true, *args)
11
+ end
12
+
13
+ machine.klass.send(:define_method, name.to_s) do |*args|
14
+ machine.fire_event(name, self, false, *args)
15
+ end
16
+ end
17
+ update(options, &block)
18
+ end
19
+
20
+ def fire(obj, to_state = nil, *args)
21
+ transitions = @transitions.select { |t| t.from == obj.current_state(@machine ? @machine.name : nil) }
22
+ raise InvalidTransition if transitions.size == 0
23
+
24
+ next_state = nil
25
+ transitions.each do |transition|
26
+ next if to_state && !Array(transition.to).include?(to_state)
27
+ if transition.perform(obj)
28
+ next_state = to_state || Array(transition.to).first
29
+ transition.execute(obj, *args)
30
+ break
31
+ end
32
+ end
33
+ next_state
34
+ end
35
+
36
+ def transitions_from_state?(state)
37
+ @transitions.any? { |t| t.from? state }
38
+ end
39
+
40
+ def ==(event)
41
+ if event.is_a? Symbol
42
+ name == event
43
+ else
44
+ name == event.name
45
+ end
46
+ end
47
+
48
+ def update(options = {}, &block)
49
+ if options.key?(:success) then @success = options[:success] end
50
+ if block then instance_eval(&block) end
51
+ self
52
+ end
53
+
54
+ private
55
+ def transitions(trans_opts)
56
+ Array(trans_opts[:from]).each do |s|
57
+ @transitions << StateTransition.new(trans_opts.merge({:from => s.to_sym}))
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,75 @@
1
+ module ActiveModel
2
+ module StateMachine
3
+ class Machine
4
+ attr_writer :initial_state
5
+ attr_accessor :states, :events, :state_index
6
+ attr_reader :klass, :name
7
+
8
+ def initialize(klass, name, options = {}, &block)
9
+ @klass, @name, @states, @state_index, @events = klass, name, [], {}, {}
10
+ update(options, &block)
11
+ end
12
+
13
+ def initial_state
14
+ @initial_state ||= (states.first ? states.first.name : nil)
15
+ end
16
+
17
+ def update(options = {}, &block)
18
+ if options.key?(:initial) then @initial_state = options[:initial] end
19
+ if block then instance_eval(&block) end
20
+ self
21
+ end
22
+
23
+ def fire_event(event, record, persist, *args)
24
+ state_index[record.current_state(@name)].call_action(:exit, record)
25
+ if new_state = @events[event].fire(record, *args)
26
+ state_index[new_state].call_action(:enter, record)
27
+
28
+ if record.respond_to?(event_fired_callback)
29
+ record.send(event_fired_callback, record.current_state, new_state)
30
+ end
31
+
32
+ record.current_state(@name, new_state, persist)
33
+ record.send(@events[event].success) if @events[event].success
34
+ true
35
+ else
36
+ if record.respond_to?(event_failed_callback)
37
+ record.send(event_failed_callback, event)
38
+ end
39
+
40
+ false
41
+ end
42
+ end
43
+
44
+ def states_for_select
45
+ states.map { |st| [st.display_name, st.name.to_s] }
46
+ end
47
+
48
+ def events_for(state)
49
+ events = @events.values.select { |event| event.transitions_from_state?(state) }
50
+ events.map! { |event| event.name }
51
+ end
52
+
53
+ def current_state_variable
54
+ "@#{@name}_current_state"
55
+ end
56
+
57
+ private
58
+ def state(name, options = {})
59
+ @states << (state_index[name] ||= State.new(name, :machine => self)).update(options)
60
+ end
61
+
62
+ def event(name, options = {}, &block)
63
+ (@events[name] ||= Event.new(self, name)).update(options, &block)
64
+ end
65
+
66
+ def event_fired_callback
67
+ @event_fired_callback ||= (@name == :default ? '' : "#{@name}_") + 'event_fired'
68
+ end
69
+
70
+ def event_failed_callback
71
+ @event_failed_callback ||= (@name == :default ? '' : "#{@name}_") + 'event_failed'
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,47 @@
1
+ module ActiveModel
2
+ module StateMachine
3
+ class State
4
+ attr_reader :name, :options
5
+
6
+ def initialize(name, options = {})
7
+ @name = name
8
+ if machine = options.delete(:machine)
9
+ machine.klass.define_state_query_method(name)
10
+ end
11
+ update(options)
12
+ end
13
+
14
+ def ==(state)
15
+ if state.is_a? Symbol
16
+ name == state
17
+ else
18
+ name == state.name
19
+ end
20
+ end
21
+
22
+ def call_action(action, record)
23
+ action = @options[action]
24
+ case action
25
+ when Symbol, String
26
+ record.send(action)
27
+ when Proc
28
+ action.call(record)
29
+ end
30
+ end
31
+
32
+ def display_name
33
+ @display_name ||= name.to_s.gsub(/_/, ' ').capitalize
34
+ end
35
+
36
+ def for_select
37
+ [display_name, name.to_s]
38
+ end
39
+
40
+ def update(options = {})
41
+ if options.key?(:display) then @display_name = options.delete(:display) end
42
+ @options = options
43
+ self
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,40 @@
1
+ module ActiveModel
2
+ module StateMachine
3
+ class StateTransition
4
+ attr_reader :from, :to, :options
5
+
6
+ def initialize(opts)
7
+ @from, @to, @guard, @on_transition = opts[:from], opts[:to], opts[:guard], opts[:on_transition]
8
+ @options = opts
9
+ end
10
+
11
+ def perform(obj)
12
+ case @guard
13
+ when Symbol, String
14
+ obj.send(@guard)
15
+ when Proc
16
+ @guard.call(obj)
17
+ else
18
+ true
19
+ end
20
+ end
21
+
22
+ def execute(obj, *args)
23
+ case @on_transition
24
+ when Symbol, String
25
+ obj.send(@on_transition, *args)
26
+ when Proc
27
+ @on_transition.call(obj, *args)
28
+ end
29
+ end
30
+
31
+ def ==(obj)
32
+ @from == obj.from && @to == obj.to
33
+ end
34
+
35
+ def from?(value)
36
+ @from == value
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,70 @@
1
+ module ActiveModel
2
+ module StateMachine
3
+ autoload :Event, 'active_model/state_machine/event'
4
+ autoload :Machine, 'active_model/state_machine/machine'
5
+ autoload :State, 'active_model/state_machine/state'
6
+ autoload :StateTransition, 'active_model/state_machine/state_transition'
7
+
8
+ extend ActiveSupport::Concern
9
+
10
+ class InvalidTransition < Exception
11
+ end
12
+
13
+ module ClassMethods
14
+ def inherited(klass)
15
+ super
16
+ klass.state_machines = state_machines
17
+ end
18
+
19
+ def state_machines
20
+ @state_machines ||= {}
21
+ end
22
+
23
+ def state_machines=(value)
24
+ @state_machines = value ? value.dup : nil
25
+ end
26
+
27
+ def state_machine(name = nil, options = {}, &block)
28
+ if name.is_a?(Hash)
29
+ options = name
30
+ name = nil
31
+ end
32
+ name ||= :default
33
+ state_machines[name] ||= Machine.new(self, name)
34
+ block ? state_machines[name].update(options, &block) : state_machines[name]
35
+ end
36
+
37
+ def define_state_query_method(state_name)
38
+ name = "#{state_name}?"
39
+ undef_method(name) if method_defined?(name)
40
+ class_eval "def #{name}; current_state.to_s == %(#{state_name}) end"
41
+ end
42
+ end
43
+
44
+ def current_state(name = nil, new_state = nil, persist = false)
45
+ sm = self.class.state_machine(name)
46
+ ivar = sm.current_state_variable
47
+ if name && new_state
48
+ if persist && respond_to?(:write_state)
49
+ write_state(sm, new_state)
50
+ end
51
+
52
+ if respond_to?(:write_state_without_persistence)
53
+ write_state_without_persistence(sm, new_state)
54
+ end
55
+
56
+ instance_variable_set(ivar, new_state)
57
+ else
58
+ instance_variable_set(ivar, nil) unless instance_variable_defined?(ivar)
59
+ value = instance_variable_get(ivar)
60
+ return value if value
61
+
62
+ if respond_to?(:read_state)
63
+ value = instance_variable_set(ivar, read_state(sm))
64
+ end
65
+
66
+ value || sm.initial_state
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,18 @@
1
+ require "active_support/test_case"
2
+
3
+ module ActiveModel #:nodoc:
4
+ class TestCase < ActiveSupport::TestCase #:nodoc:
5
+ def with_kcode(kcode)
6
+ if RUBY_VERSION < '1.9'
7
+ orig_kcode, $KCODE = $KCODE, kcode
8
+ begin
9
+ yield
10
+ ensure
11
+ $KCODE = orig_kcode
12
+ end
13
+ else
14
+ yield
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,44 @@
1
+ require 'active_support/core_ext/hash/reverse_merge'
2
+
3
+ module ActiveModel
4
+ module Translation
5
+ include ActiveModel::Naming
6
+
7
+ # Returns the i18n_scope for the class. Overwrite if you want custom lookup.
8
+ def i18n_scope
9
+ :activemodel
10
+ end
11
+
12
+ # When localizing a string, goes through the lookup returned by this method.
13
+ # Used in ActiveModel::Name#human, ActiveModel::Errors#full_messages and
14
+ # ActiveModel::Translation#human_attribute_name.
15
+ def lookup_ancestors
16
+ self.ancestors.select { |x| x.respond_to?(:model_name) }
17
+ end
18
+
19
+ # Transforms attributes names into a more human format, such as "First name" instead of "first_name".
20
+ #
21
+ # Example:
22
+ #
23
+ # Person.human_attribute_name("first_name") # => "First name"
24
+ #
25
+ # Specify +options+ with additional translating options.
26
+ def human_attribute_name(attribute, options = {})
27
+ defaults = lookup_ancestors.map do |klass|
28
+ :"#{klass.model_name.underscore}.#{attribute}"
29
+ end
30
+
31
+ defaults << options.delete(:default) if options[:default]
32
+ defaults << attribute.to_s.humanize
33
+
34
+ options.reverse_merge! :scope => [self.i18n_scope, :attributes], :count => 1, :default => defaults
35
+ I18n.translate(defaults.shift, options)
36
+ end
37
+
38
+ # Model.human_name is deprecated. Use Model.model_name.human instead.
39
+ def human_name(*args)
40
+ ActiveSupport::Deprecation.warn("human_name has been deprecated, please use model_name.human instead", caller[0,5])
41
+ model_name.human(*args)
42
+ end
43
+ end
44
+ end