cassandra_object 0.6.0.pre

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.
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