seomoz-ripple 1.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. data/Gemfile +20 -0
  2. data/Guardfile +15 -0
  3. data/Rakefile +88 -0
  4. data/lib/rails/generators/ripple/configuration/configuration_generator.rb +13 -0
  5. data/lib/rails/generators/ripple/configuration/templates/ripple.yml +24 -0
  6. data/lib/rails/generators/ripple/js/js_generator.rb +13 -0
  7. data/lib/rails/generators/ripple/js/templates/js/contrib.js +63 -0
  8. data/lib/rails/generators/ripple/js/templates/js/iso8601.js +76 -0
  9. data/lib/rails/generators/ripple/js/templates/js/ripple.js +132 -0
  10. data/lib/rails/generators/ripple/model/model_generator.rb +20 -0
  11. data/lib/rails/generators/ripple/model/templates/model.rb +10 -0
  12. data/lib/rails/generators/ripple/observer/observer_generator.rb +16 -0
  13. data/lib/rails/generators/ripple/observer/templates/observer.rb +4 -0
  14. data/lib/rails/generators/ripple/test/templates/test_server.rb +46 -0
  15. data/lib/rails/generators/ripple/test/test_generator.rb +39 -0
  16. data/lib/rails/generators/ripple_generator.rb +78 -0
  17. data/lib/ripple.rb +79 -0
  18. data/lib/ripple/associations.rb +356 -0
  19. data/lib/ripple/associations/embedded.rb +35 -0
  20. data/lib/ripple/associations/instantiators.rb +26 -0
  21. data/lib/ripple/associations/linked.rb +65 -0
  22. data/lib/ripple/associations/many.rb +38 -0
  23. data/lib/ripple/associations/many_embedded_proxy.rb +38 -0
  24. data/lib/ripple/associations/many_linked_proxy.rb +66 -0
  25. data/lib/ripple/associations/many_reference_proxy.rb +93 -0
  26. data/lib/ripple/associations/many_stored_key_proxy.rb +76 -0
  27. data/lib/ripple/associations/one.rb +20 -0
  28. data/lib/ripple/associations/one_embedded_proxy.rb +35 -0
  29. data/lib/ripple/associations/one_key_proxy.rb +58 -0
  30. data/lib/ripple/associations/one_linked_proxy.rb +22 -0
  31. data/lib/ripple/associations/one_stored_key_proxy.rb +43 -0
  32. data/lib/ripple/associations/proxy.rb +118 -0
  33. data/lib/ripple/attribute_methods.rb +118 -0
  34. data/lib/ripple/attribute_methods/dirty.rb +50 -0
  35. data/lib/ripple/attribute_methods/query.rb +34 -0
  36. data/lib/ripple/attribute_methods/read.rb +26 -0
  37. data/lib/ripple/attribute_methods/write.rb +25 -0
  38. data/lib/ripple/callbacks.rb +74 -0
  39. data/lib/ripple/conflict/basic_resolver.rb +82 -0
  40. data/lib/ripple/conflict/document_hooks.rb +20 -0
  41. data/lib/ripple/conflict/resolver.rb +71 -0
  42. data/lib/ripple/conflict/test_helper.rb +33 -0
  43. data/lib/ripple/conversion.rb +28 -0
  44. data/lib/ripple/core_ext.rb +2 -0
  45. data/lib/ripple/core_ext/casting.rb +148 -0
  46. data/lib/ripple/core_ext/object.rb +8 -0
  47. data/lib/ripple/document.rb +104 -0
  48. data/lib/ripple/document/bucket_access.rb +25 -0
  49. data/lib/ripple/document/finders.rb +131 -0
  50. data/lib/ripple/document/key.rb +43 -0
  51. data/lib/ripple/document/link.rb +30 -0
  52. data/lib/ripple/document/persistence.rb +115 -0
  53. data/lib/ripple/embedded_document.rb +64 -0
  54. data/lib/ripple/embedded_document/around_callbacks.rb +18 -0
  55. data/lib/ripple/embedded_document/finders.rb +26 -0
  56. data/lib/ripple/embedded_document/persistence.rb +77 -0
  57. data/lib/ripple/i18n.rb +2 -0
  58. data/lib/ripple/inspection.rb +32 -0
  59. data/lib/ripple/locale/en.yml +21 -0
  60. data/lib/ripple/nested_attributes.rb +265 -0
  61. data/lib/ripple/observable.rb +28 -0
  62. data/lib/ripple/properties.rb +73 -0
  63. data/lib/ripple/property_type_mismatch.rb +12 -0
  64. data/lib/ripple/railtie.rb +13 -0
  65. data/lib/ripple/serialization.rb +84 -0
  66. data/lib/ripple/timestamps.rb +27 -0
  67. data/lib/ripple/translation.rb +14 -0
  68. data/lib/ripple/validations.rb +67 -0
  69. data/lib/ripple/validations/associated_validator.rb +43 -0
  70. data/ripple.gemspec +46 -0
  71. data/seomoz-ripple.gemspec +46 -0
  72. data/spec/fixtures/config.yml +8 -0
  73. data/spec/integration/ripple/associations_spec.rb +220 -0
  74. data/spec/integration/ripple/conflict_resolution_spec.rb +293 -0
  75. data/spec/integration/ripple/nested_attributes_spec.rb +264 -0
  76. data/spec/integration/ripple/persistence_spec.rb +57 -0
  77. data/spec/integration/ripple/search_associations_spec.rb +42 -0
  78. data/spec/ripple/associations/many_embedded_proxy_spec.rb +122 -0
  79. data/spec/ripple/associations/many_linked_proxy_spec.rb +191 -0
  80. data/spec/ripple/associations/many_reference_proxy_spec.rb +170 -0
  81. data/spec/ripple/associations/many_stored_key_proxy_spec.rb +158 -0
  82. data/spec/ripple/associations/one_embedded_proxy_spec.rb +125 -0
  83. data/spec/ripple/associations/one_key_proxy_spec.rb +82 -0
  84. data/spec/ripple/associations/one_linked_proxy_spec.rb +91 -0
  85. data/spec/ripple/associations/one_stored_key_proxy_spec.rb +72 -0
  86. data/spec/ripple/associations/proxy_spec.rb +84 -0
  87. data/spec/ripple/associations_spec.rb +129 -0
  88. data/spec/ripple/attribute_methods/dirty_spec.rb +80 -0
  89. data/spec/ripple/attribute_methods_spec.rb +230 -0
  90. data/spec/ripple/bucket_access_spec.rb +25 -0
  91. data/spec/ripple/callbacks_spec.rb +176 -0
  92. data/spec/ripple/conflict/resolver_spec.rb +42 -0
  93. data/spec/ripple/conversion_spec.rb +22 -0
  94. data/spec/ripple/core_ext_spec.rb +103 -0
  95. data/spec/ripple/document/link_spec.rb +67 -0
  96. data/spec/ripple/document_spec.rb +96 -0
  97. data/spec/ripple/embedded_document/finders_spec.rb +29 -0
  98. data/spec/ripple/embedded_document/persistence_spec.rb +80 -0
  99. data/spec/ripple/embedded_document_spec.rb +84 -0
  100. data/spec/ripple/finders_spec.rb +217 -0
  101. data/spec/ripple/inspection_spec.rb +51 -0
  102. data/spec/ripple/key_spec.rb +30 -0
  103. data/spec/ripple/observable_spec.rb +121 -0
  104. data/spec/ripple/persistence_spec.rb +326 -0
  105. data/spec/ripple/properties_spec.rb +262 -0
  106. data/spec/ripple/ripple_spec.rb +71 -0
  107. data/spec/ripple/serialization_spec.rb +51 -0
  108. data/spec/ripple/timestamps_spec.rb +76 -0
  109. data/spec/ripple/validations/associated_validator_spec.rb +77 -0
  110. data/spec/ripple/validations_spec.rb +104 -0
  111. data/spec/spec_helper.rb +26 -0
  112. data/spec/support/associations.rb +1 -0
  113. data/spec/support/associations/proxies.rb +17 -0
  114. data/spec/support/integration_setup.rb +11 -0
  115. data/spec/support/mocks.rb +4 -0
  116. data/spec/support/models.rb +4 -0
  117. data/spec/support/models/address.rb +12 -0
  118. data/spec/support/models/box.rb +13 -0
  119. data/spec/support/models/car.rb +16 -0
  120. data/spec/support/models/cardboard_box.rb +3 -0
  121. data/spec/support/models/clock.rb +12 -0
  122. data/spec/support/models/clock_observer.rb +3 -0
  123. data/spec/support/models/company.rb +23 -0
  124. data/spec/support/models/customer.rb +4 -0
  125. data/spec/support/models/driver.rb +6 -0
  126. data/spec/support/models/email.rb +4 -0
  127. data/spec/support/models/engine.rb +5 -0
  128. data/spec/support/models/family.rb +16 -0
  129. data/spec/support/models/favorite.rb +4 -0
  130. data/spec/support/models/invoice.rb +7 -0
  131. data/spec/support/models/late_invoice.rb +3 -0
  132. data/spec/support/models/ninja.rb +9 -0
  133. data/spec/support/models/note.rb +5 -0
  134. data/spec/support/models/page.rb +4 -0
  135. data/spec/support/models/paid_invoice.rb +4 -0
  136. data/spec/support/models/passenger.rb +6 -0
  137. data/spec/support/models/profile.rb +10 -0
  138. data/spec/support/models/seat.rb +5 -0
  139. data/spec/support/models/subscription.rb +27 -0
  140. data/spec/support/models/tasks.rb +14 -0
  141. data/spec/support/models/team.rb +11 -0
  142. data/spec/support/models/transactions.rb +17 -0
  143. data/spec/support/models/tree.rb +4 -0
  144. data/spec/support/models/user.rb +10 -0
  145. data/spec/support/models/wheel.rb +6 -0
  146. data/spec/support/models/widget.rb +22 -0
  147. data/spec/support/test_server.rb +18 -0
  148. data/spec/support/test_server.yml.example +2 -0
  149. metadata +362 -0
@@ -0,0 +1,34 @@
1
+ require 'active_support/concern'
2
+
3
+ module Ripple
4
+ module AttributeMethods
5
+ module Query
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ attribute_method_suffix "?"
10
+ end
11
+
12
+ private
13
+ # Based on code from ActiveRecord
14
+ def attribute?(attr_name)
15
+ unless value = attribute(attr_name)
16
+ false
17
+ else
18
+ prop = self.class.properties[attr_name]
19
+ if prop.nil?
20
+ if Numeric === value || value !~ /[^0-9]/
21
+ !value.to_i.zero?
22
+ else
23
+ Boolean.ripple_cast(value) || value.present?
24
+ end
25
+ elsif prop.type <= Numeric
26
+ !value.zero?
27
+ else
28
+ value.present?
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,26 @@
1
+ require 'active_support/concern'
2
+
3
+ module Ripple
4
+ module AttributeMethods
5
+ module Read
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ attribute_method_suffix ""
10
+ end
11
+
12
+ def [](attr_name)
13
+ attribute(attr_name)
14
+ end
15
+
16
+ private
17
+ def attribute(attr_name)
18
+ if @attributes.include?(attr_name)
19
+ @attributes[attr_name]
20
+ else
21
+ nil
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,25 @@
1
+ require 'active_support/concern'
2
+
3
+ module Ripple
4
+ module AttributeMethods
5
+ module Write
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ attribute_method_suffix "="
10
+ end
11
+
12
+ def []=(attr_name, value)
13
+ __send__(:attribute=, attr_name, value)
14
+ end
15
+
16
+ private
17
+ def attribute=(attr_name, value)
18
+ if prop = self.class.properties[attr_name]
19
+ value = prop.type_cast(value)
20
+ end
21
+ @attributes[attr_name] = value
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,74 @@
1
+ require 'active_support/concern'
2
+ require 'active_model/callbacks'
3
+
4
+ module Ripple
5
+ # Adds lifecycle callbacks to {Ripple::Document} models, in the typical
6
+ # ActiveModel fashion.
7
+ module Callbacks
8
+ extend ActiveSupport::Concern
9
+
10
+ CALLBACK_TYPES = [:create, :update, :save, :destroy, :validation]
11
+
12
+ included do
13
+ extend ActiveModel::Callbacks
14
+ define_model_callbacks *(CALLBACK_TYPES - [:validation])
15
+ define_callbacks :validation, :terminator => "result == false", :scope => [:kind, :name]
16
+ end
17
+
18
+ module ClassMethods
19
+ # Defines a callback to be run before validations.
20
+ def before_validation(*args, &block)
21
+ options = args.last
22
+ if options.is_a?(Hash) && options[:on]
23
+ options[:if] = Array(options[:if])
24
+ options[:if] << "@_on_validate == :#{options[:on]}"
25
+ end
26
+ set_callback(:validation, :before, *args, &block)
27
+ end
28
+
29
+ # Defines a callback to be run after validations.
30
+ def after_validation(*args, &block)
31
+ options = args.extract_options!
32
+ options[:prepend] = true
33
+ options[:if] = Array(options[:if])
34
+ options[:if] << "!halted && value != false"
35
+ options[:if] << "@_on_validate == :#{options[:on]}" if options[:on]
36
+ set_callback(:validation, :after, *(args << options), &block)
37
+ end
38
+ end
39
+
40
+ # @private
41
+ module InstanceMethods
42
+ # @private
43
+ def really_save(*args, &block)
44
+ run_save_callbacks do
45
+ super
46
+ end
47
+ end
48
+
49
+ def run_save_callbacks
50
+ state = new? ? :create : :update
51
+ run_callbacks(:save) do
52
+ run_callbacks(state) do
53
+ yield
54
+ end
55
+ end
56
+ end
57
+
58
+ # @private
59
+ def destroy(*args, &block)
60
+ run_callbacks(:destroy) do
61
+ super
62
+ end
63
+ end
64
+
65
+ # @private
66
+ def valid?(*args, &block)
67
+ @_on_validate = new? ? :create : :update
68
+ run_callbacks(:validation) do
69
+ super
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,82 @@
1
+ module Ripple
2
+ module Conflict
3
+ class BasicResolver
4
+ delegate :model_class, :document, :siblings, :to => :@main_resolver
5
+
6
+ def initialize(main_resolver)
7
+ @main_resolver = main_resolver
8
+ end
9
+
10
+ def remaining_conflicts
11
+ @remaining_conflicts ||= []
12
+ end
13
+
14
+ def unexpected_conflicts
15
+ # if the user didn't specify the conflict they expect,
16
+ # then don't consider any conflicts unexpected
17
+ return [] if model_class.expected_conflicts.blank?
18
+
19
+ remaining_conflicts - model_class.expected_conflicts
20
+ end
21
+
22
+ def perform
23
+ process_properties
24
+ process_embedded_associations
25
+ process_linked_associations
26
+ process_stored_key_associations
27
+ end
28
+
29
+ private
30
+
31
+ def process_properties
32
+ model_class.properties.each do |name, property|
33
+ document.send(:"#{name}=", resolved_property_value_for(property))
34
+ end
35
+ end
36
+
37
+ def process_embedded_associations
38
+ model_class.embedded_associations.each do |assoc|
39
+ document.send(:"#{assoc.name}=", resolved_association_value_for(assoc, :load_target))
40
+ end
41
+ end
42
+
43
+ def process_linked_associations
44
+ model_class.linked_associations.each do |assoc|
45
+ document.send(assoc.name).replace_links(resolved_association_value_for(assoc, :links))
46
+ end
47
+ end
48
+
49
+ def process_stored_key_associations
50
+ model_class.stored_key_associations.each do |assoc|
51
+ document.send(assoc.name).reset_owner_keys if (assoc.type == :many)
52
+ end
53
+ end
54
+
55
+ def resolved_property_value_for(property)
56
+ uniq_values = siblings.map(&property.key).uniq
57
+
58
+ value = if uniq_values.size == 1
59
+ uniq_values.first
60
+ elsif property.key == :updated_at
61
+ uniq_values.compact.max
62
+ elsif property.key == :created_at
63
+ uniq_values.compact.min
64
+ else
65
+ remaining_conflicts << property.key
66
+ property.default
67
+ end
68
+ end
69
+
70
+ def resolved_association_value_for(association, proxy_value_method)
71
+ # the association proxy doesn't uniquify well, so we have to use the target or links directly
72
+ uniq_values = siblings.map { |s| s.send(association.name).__send__(proxy_value_method) }.uniq
73
+
74
+ return uniq_values.first if uniq_values.size == 1
75
+ remaining_conflicts << association.name
76
+
77
+ association.many? ? [] : nil # default value
78
+ end
79
+ end
80
+ end
81
+ end
82
+
@@ -0,0 +1,20 @@
1
+ module Ripple
2
+ module Conflict
3
+ module DocumentHooks
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ attr_reader :on_conflict_block
8
+
9
+ def on_conflict(*expected_conflicts, &block)
10
+ @expected_conflicts = expected_conflicts
11
+ @on_conflict_block = block
12
+ end
13
+
14
+ def expected_conflicts
15
+ @expected_conflicts ||= []
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,71 @@
1
+ require 'ripple/conflict/basic_resolver'
2
+
3
+ module Ripple
4
+ module Conflict
5
+ class Resolver
6
+ include Translation
7
+
8
+ attr_reader :document, :model_class
9
+
10
+ delegate :expected_conflicts, :on_conflict_block, :to => :model_class
11
+
12
+ def self.to_proc
13
+ @to_proc ||= lambda do |robject|
14
+ possible_model_classes = robject.siblings.map { |s| s.data['_type'] }.uniq
15
+ return nil unless possible_model_classes.size == 1
16
+
17
+ resolver = new(robject, possible_model_classes.first.constantize)
18
+ resolver.resolve
19
+ resolver.document.robject
20
+ end
21
+ end
22
+
23
+ Riak::RObject.on_conflict(&self)
24
+
25
+ def initialize(robject, model_class)
26
+ @robject = robject
27
+ @model_class = model_class
28
+ end
29
+
30
+ def resolve
31
+ assert_conflict_block
32
+ basic_resolver.perform
33
+ assert_no_unexpected_conflicts
34
+ document.instance_exec(siblings, basic_resolver.remaining_conflicts, &on_conflict_block)
35
+ document.update_robject
36
+ end
37
+
38
+ def siblings
39
+ @siblings ||= @robject.siblings.map { |s| @model_class.send(:instantiate, s) }
40
+ end
41
+
42
+ def document
43
+ # pick a sibling robject to use as the basis of the document to resolve
44
+ # which one doesn't really matter.
45
+ @document ||= @model_class.send(:instantiate, @robject.siblings.first.dup)
46
+ end
47
+
48
+ private
49
+
50
+ def basic_resolver
51
+ @basic_resolver ||= BasicResolver.new(self)
52
+ end
53
+
54
+ def assert_conflict_block
55
+ return if on_conflict_block
56
+
57
+ raise NotImplementedError, t('conflict_handler_not_implemented', :document => document)
58
+ end
59
+
60
+ def assert_no_unexpected_conflicts
61
+ return unless basic_resolver.unexpected_conflicts.any?
62
+
63
+ raise NotImplementedError, t('unexpected_conflicts',
64
+ :conflicts => basic_resolver.unexpected_conflicts.inspect,
65
+ :document => document.inspect
66
+ )
67
+ end
68
+ end
69
+ end
70
+ end
71
+
@@ -0,0 +1,33 @@
1
+ module Ripple
2
+ module Conflict
3
+ module TestHelper
4
+ def create_conflict(main_record, *modifiers)
5
+ # We have to disable all on conflict resolvers while we create conflict
6
+ # so that they don't auto-resolve it.
7
+ orig_hooks = Riak::RObject.on_conflict_hooks.dup
8
+ Riak::RObject.on_conflict_hooks.clear
9
+
10
+ begin
11
+ klass, key = main_record.class, main_record.key
12
+ records = modifiers.map { |_| klass.find!(key) }
13
+
14
+ records.zip(modifiers).each do |(record, modifier)|
15
+ # necessary to get conflict, so riak thinks they are being saved by different clients
16
+ Ripple.client.client_id += 1
17
+
18
+ modifier.call(record)
19
+ record.save!
20
+ end
21
+
22
+ robject = klass.bucket.get(key)
23
+ raise "#{robject} is not in conflict as expected." unless robject.conflict?
24
+ ensure
25
+ orig_hooks.each do |hook|
26
+ Riak::RObject.on_conflict(&hook)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+
@@ -0,0 +1,28 @@
1
+ require 'active_model/conversion'
2
+
3
+ module Ripple
4
+ # Provides ActionPack compatibility for {Ripple::Document} models.
5
+ module Conversion
6
+ include ActiveModel::Conversion
7
+
8
+ # True if this is a new document
9
+ def new_record?
10
+ new?
11
+ end
12
+
13
+ # True if this is not a new document
14
+ def persisted?
15
+ !new?
16
+ end
17
+
18
+ # Converts to a view key
19
+ def to_key
20
+ new? ? nil : [key]
21
+ end
22
+
23
+ # Converts to a URL parameter
24
+ def to_param
25
+ key
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,2 @@
1
+ require 'ripple/core_ext/casting'
2
+ require 'ripple/core_ext/object'
@@ -0,0 +1,148 @@
1
+ require 'active_support/json'
2
+ require 'active_support/core_ext/object/blank'
3
+ require 'active_support/core_ext/object/to_json'
4
+ require 'active_support/core_ext/date/conversions'
5
+ require 'active_support/core_ext/date/zones'
6
+ require 'active_support/core_ext/date_time/conversions'
7
+ require 'active_support/core_ext/date_time/zones'
8
+ require 'active_support/core_ext/time/conversions'
9
+ require 'active_support/core_ext/time/zones'
10
+ require 'active_support/core_ext/string/conversions'
11
+ require 'ripple/property_type_mismatch'
12
+ require 'set'
13
+
14
+ # @private
15
+ class Object
16
+ def self.ripple_cast(value)
17
+ value
18
+ end
19
+ end
20
+
21
+ # @private
22
+ class Symbol
23
+ def self.ripple_cast(value)
24
+ return nil if value.blank?
25
+ value.respond_to?(:to_s) && value.to_s.intern or raise Ripple::PropertyTypeMismatch.new(self, value)
26
+ end
27
+ end
28
+
29
+ # @private
30
+ class Numeric
31
+ def self.ripple_cast(value)
32
+ return nil if value.blank?
33
+ raise Ripple::PropertyTypeMismatch.new(self,value) unless value.respond_to?(:to_i) && value.respond_to?(:to_f)
34
+ float_value = value.to_f
35
+ int_value = value.to_i
36
+ float_value == int_value ? int_value : float_value
37
+ end
38
+ end
39
+
40
+ # @private
41
+ class Integer
42
+ def self.ripple_cast(value)
43
+ return nil if value.nil? || (String === value && value.blank?)
44
+ !value.is_a?(Symbol) && value.respond_to?(:to_i) && value.to_i or raise Ripple::PropertyTypeMismatch.new(self, value)
45
+ end
46
+ end
47
+
48
+ # @private
49
+ class Float
50
+ def self.ripple_cast(value)
51
+ return nil if value.nil? || (String === value && value.blank?)
52
+ value.respond_to?(:to_f) && value.to_f or raise Ripple::PropertyTypeMismatch.new(self, value)
53
+ end
54
+ end
55
+
56
+ # @private
57
+ class String
58
+ def self.ripple_cast(value)
59
+ return nil if value.nil?
60
+ value.respond_to?(:to_s) && value.to_s or raise Ripple::PropertyTypeMismatch.new(self, value)
61
+ end
62
+ end
63
+
64
+ boolean_cast = proc do
65
+ def self.ripple_cast(value)
66
+ case value
67
+ when NilClass
68
+ nil
69
+ when Numeric
70
+ !value.zero?
71
+ when TrueClass, FalseClass
72
+ value
73
+ when /^\s*t/i
74
+ true
75
+ when /^\s*f/i
76
+ false
77
+ else
78
+ value.present?
79
+ end
80
+ end
81
+ end
82
+
83
+ unless defined?(::Boolean)
84
+ # Stand-in for true/false property types.
85
+ module ::Boolean; end
86
+ end
87
+
88
+ ::Boolean.module_eval(&boolean_cast)
89
+ TrueClass.module_eval(&boolean_cast)
90
+ FalseClass.module_eval(&boolean_cast)
91
+
92
+ # @private
93
+ class Time
94
+ def as_json(options={})
95
+ self.utc.send(Ripple.date_format)
96
+ end
97
+
98
+ def self.ripple_cast(value)
99
+ return nil if value.blank?
100
+ value.respond_to?(:to_time) && value.to_time or raise Ripple::PropertyTypeMismatch.new(self, value)
101
+ end
102
+ end
103
+
104
+ # @private
105
+ class Date
106
+ def as_json(options={})
107
+ self.to_s(Ripple.date_format)
108
+ end
109
+
110
+ def self.ripple_cast(value)
111
+ return nil if value.blank?
112
+ value.respond_to?(:to_date) && value.to_date or raise Ripple::PropertyTypeMismatch.new(self, value)
113
+ end
114
+ end
115
+
116
+ # @private
117
+ class DateTime
118
+ def as_json(options={})
119
+ self.utc.to_s(Ripple.date_format)
120
+ end
121
+
122
+ def self.ripple_cast(value)
123
+ return nil if value.blank?
124
+ value.respond_to?(:to_datetime) && value.to_datetime or raise Ripple::PropertyTypeMismatch.new(self, value)
125
+ end
126
+ end
127
+
128
+ # @private
129
+ module ActiveSupport
130
+ class TimeWithZone
131
+ def as_json(options={})
132
+ self.utc.send(Ripple.date_format)
133
+ end
134
+ end
135
+ end
136
+
137
+ # @private
138
+ class Set
139
+ def as_json(options = {})
140
+ map { |e| e.as_json(options) }
141
+ end
142
+
143
+ def self.ripple_cast(value)
144
+ return nil if value.nil?
145
+ value.is_a?(Enumerable) && new(value) or raise Ripple::PropertyTypeMismatch.new(self, value)
146
+ end
147
+ end
148
+