activeobject 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. data/CHANGE +10 -0
  2. data/Interface_desc +21 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README +72 -0
  5. data/Rakefile.rb +9 -0
  6. data/active-object.gemspec +50 -0
  7. data/examples/account.rb +69 -0
  8. data/examples/data.tch +0 -0
  9. data/examples/light_cloud.yml +18 -0
  10. data/examples/test.rb +3 -0
  11. data/examples/user.rb +112 -0
  12. data/init.rb +4 -0
  13. data/lib/active-object.rb +23 -0
  14. data/lib/active_object/adapters/light_cloud.rb +40 -0
  15. data/lib/active_object/adapters/tokyo_cabinet.rb +48 -0
  16. data/lib/active_object/adapters/tokyo_tyrant.rb +14 -0
  17. data/lib/active_object/associations.rb +200 -0
  18. data/lib/active_object/base.rb +415 -0
  19. data/lib/active_object/callbacks.rb +180 -0
  20. data/lib/active_object/observer.rb +180 -0
  21. data/lib/active_object/serialization.rb +99 -0
  22. data/lib/active_object/serializers/json_serializer.rb +75 -0
  23. data/lib/active_object/serializers/xml_serializer.rb +325 -0
  24. data/lib/active_object/validations.rb +687 -0
  25. data/lib/active_support/callbacks.rb +303 -0
  26. data/lib/active_support/core_ext/array/access.rb +53 -0
  27. data/lib/active_support/core_ext/array/conversions.rb +183 -0
  28. data/lib/active_support/core_ext/array/extract_options.rb +20 -0
  29. data/lib/active_support/core_ext/array/grouping.rb +106 -0
  30. data/lib/active_support/core_ext/array/random_access.rb +12 -0
  31. data/lib/active_support/core_ext/array.rb +13 -0
  32. data/lib/active_support/core_ext/blank.rb +58 -0
  33. data/lib/active_support/core_ext/class/attribute_accessors.rb +54 -0
  34. data/lib/active_support/core_ext/class/inheritable_attributes.rb +140 -0
  35. data/lib/active_support/core_ext/class/removal.rb +50 -0
  36. data/lib/active_support/core_ext/class.rb +3 -0
  37. data/lib/active_support/core_ext/duplicable.rb +43 -0
  38. data/lib/active_support/core_ext/enumerable.rb +72 -0
  39. data/lib/active_support/core_ext/hash/conversions.rb +259 -0
  40. data/lib/active_support/core_ext/hash/keys.rb +52 -0
  41. data/lib/active_support/core_ext/hash.rb +8 -0
  42. data/lib/active_support/core_ext/module/aliasing.rb +74 -0
  43. data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +31 -0
  44. data/lib/active_support/core_ext/module/attribute_accessors.rb +58 -0
  45. data/lib/active_support/core_ext/module.rb +16 -0
  46. data/lib/active_support/core_ext/object/conversions.rb +14 -0
  47. data/lib/active_support/core_ext/object/extending.rb +80 -0
  48. data/lib/active_support/core_ext/object/instance_variables.rb +74 -0
  49. data/lib/active_support/core_ext/object/metaclass.rb +13 -0
  50. data/lib/active_support/core_ext/object/misc.rb +43 -0
  51. data/lib/active_support/core_ext/object.rb +5 -0
  52. data/lib/active_support/core_ext/string/inflections.rb +167 -0
  53. data/lib/active_support/core_ext/string.rb +7 -0
  54. data/lib/active_support/core_ext.rb +4 -0
  55. data/lib/active_support/inflections.rb +55 -0
  56. data/lib/active_support/inflector.rb +348 -0
  57. data/lib/active_support/vendor/builder-2.1.2/blankslate.rb +113 -0
  58. data/lib/active_support/vendor/builder-2.1.2/builder/blankslate.rb +20 -0
  59. data/lib/active_support/vendor/builder-2.1.2/builder/css.rb +250 -0
  60. data/lib/active_support/vendor/builder-2.1.2/builder/xchar.rb +115 -0
  61. data/lib/active_support/vendor/builder-2.1.2/builder/xmlbase.rb +139 -0
  62. data/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb +63 -0
  63. data/lib/active_support/vendor/builder-2.1.2/builder/xmlmarkup.rb +328 -0
  64. data/lib/active_support/vendor/builder-2.1.2/builder.rb +13 -0
  65. data/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb +1021 -0
  66. data/lib/active_support/vendor.rb +14 -0
  67. data/lib/active_support.rb +6 -0
  68. data/spec/case/association_test.rb +97 -0
  69. data/spec/case/base_test.rb +74 -0
  70. data/spec/case/callbacks_observers_test.rb +38 -0
  71. data/spec/case/callbacks_test.rb +424 -0
  72. data/spec/case/serialization_test.rb +87 -0
  73. data/spec/case/validations_test.rb +1482 -0
  74. data/spec/data.tch +0 -0
  75. data/spec/helper.rb +15 -0
  76. data/spec/light_cloud.yml +18 -0
  77. data/spec/model/account.rb +4 -0
  78. data/spec/model/topic.rb +26 -0
  79. data/spec/model/user.rb +8 -0
  80. metadata +173 -0
@@ -0,0 +1,180 @@
1
+ require 'observer'
2
+
3
+ module ActiveObject
4
+ # Callbacks是一个Ant Mapper对象生命周期内的钩子函数,允许你在对象状态改变的之前或之后触发相关规则.
5
+
6
+ # 当一个新对象的 <tt>Base#save</tt> 方法被调用时:
7
+ #
8
+ # * (-) <tt>save</tt>
9
+ # * (-) <tt>valid</tt>
10
+ # * (1) <tt>before_validation</tt>
11
+ # * (2) <tt>before_validation_on_create</tt>
12
+ # * (-) <tt>validate</tt>
13
+ # * (-) <tt>validate_on_create</tt>
14
+ # * (3) <tt>after_validation</tt>
15
+ # * (4) <tt>after_validation_on_create</tt>
16
+ # * (5) <tt>before_save</tt>
17
+ # * (6) <tt>before_create</tt>
18
+ # * (-) <tt>create</tt>
19
+ # * (7) <tt>after_create</tt>
20
+ # * (8) <tt>after_save</tt>
21
+ #
22
+ # 这里总共有8个回调.已经存在的记录调用<tt>Base#save</tt>时,除了<tt>_on_create</tt> 回调被<tt>_on_update</tt>取代外,其它都一样.
23
+ #
24
+ # 示例:
25
+ # class CreditCard < ActiveObject::Base
26
+ # # Strip everything but digits, so the user can specify "555 234 34" or
27
+ # # "5552-3434" or both will mean "55523434"
28
+ # def before_validation_on_create
29
+ # self.number = number.gsub(/[^0-9]/, "") if attribute_present?("number")
30
+ # end
31
+ # end
32
+ #
33
+ # class Subscription < ActiveObject::Base
34
+ # before_create :record_signup
35
+ #
36
+ # private
37
+ # def record_signup
38
+ # self.signed_up_on = Date.today
39
+ # end
40
+ # end
41
+ #
42
+ #
43
+ # == 继承回调序列
44
+ #
45
+ # 除了重写回调方法外,它也可以通过使用回调宏注册回调.宏能添加行为到回调序列,这样在多级继承时,能保留回调序列的完整。
46
+ # 示例:
47
+ #
48
+ # class Topic < ActiveObject::Base
49
+ # before_destroy :destroy_author
50
+ # end
51
+ #
52
+ # class Reply < Topic
53
+ # before_destroy :destroy_readers
54
+ # end
55
+ #
56
+ # 现在, 当 <tt>Topic#destroy</tt> 运行时只调用 +destroy_author+.当<tt>Reply#destroy</tt>运行时,将调用+destroy_author+ 和 # +destroy_readers+ 。
57
+ #
58
+ #
59
+ module Callbacks
60
+ CALLBACKS = %w(
61
+ after_find after_initialize before_save after_save before_create after_create before_update after_update before_validation
62
+ after_validation before_validation_on_create after_validation_on_create before_validation_on_update
63
+ after_validation_on_update before_destroy after_destroy
64
+ )
65
+
66
+ def self.included(base) #:nodoc:
67
+ base.extend Observable
68
+
69
+ [:initialize,:create_or_update, :valid?, :create, :update, :destroy].each do |method|
70
+ base.send :alias_method_chain, method, :callbacks
71
+ end
72
+
73
+ base.send :include, ActiveSupport::Callbacks
74
+ base.define_callbacks *CALLBACKS
75
+ end
76
+
77
+ # 在调用<tt>Base.new</tt>时,对象被初始化之后执行。
78
+ def after_initialize() end
79
+
80
+ def initialize_with_callbacks(*args)
81
+ result = initialize_without_callbacks(*args)
82
+ callback(:after_initialize)
83
+ result
84
+ end
85
+
86
+ # 在执行<tt>Base.save</tt>之前被调用 (不管是创建还是更新).
87
+ def before_save() end
88
+
89
+ # 在执行<tt>Base.save</tt>被调用(不管是创建还是更新).
90
+ #
91
+ # class Contact < ActiveObject::Base
92
+ # after_save { logger.info( 'New contact saved!' ) }
93
+ # end
94
+ def after_save() end
95
+ def create_or_update_with_callbacks #:nodoc:
96
+ return false if callback(:before_save) == false
97
+ result = create_or_update_without_callbacks
98
+ callback(:after_save)
99
+ result
100
+ end
101
+ private :create_or_update_with_callbacks
102
+
103
+
104
+ def before_create() end
105
+
106
+ def after_create() end
107
+ def create_with_callbacks #:nodoc:
108
+ return false if callback(:before_create) == false
109
+ result = create_without_callbacks
110
+ callback(:after_create)
111
+ result
112
+ end
113
+ private :create_with_callbacks
114
+
115
+ def before_update() end
116
+
117
+ def after_update() end
118
+
119
+ def update_with_callbacks(*args) #:nodoc:
120
+ return false if callback(:before_update) == false
121
+ result = update_without_callbacks(*args)
122
+ callback(:after_update)
123
+ result
124
+ end
125
+ private :update_with_callbacks
126
+
127
+ def before_validation() end
128
+
129
+ def after_validation() end
130
+
131
+ def before_validation_on_create() end
132
+
133
+ def after_validation_on_create() end
134
+
135
+ def before_validation_on_update() end
136
+
137
+ def after_validation_on_update() end
138
+
139
+ def valid_with_callbacks? #:nodoc:
140
+ return false if callback(:before_validation) == false
141
+ if new_record? then result = callback(:before_validation_on_create) else result = callback(:before_validation_on_update) end
142
+ return false if false == result
143
+
144
+ result = valid_without_callbacks?
145
+
146
+ callback(:after_validation)
147
+ if new_record? then callback(:after_validation_on_create) else callback(:after_validation_on_update) end
148
+
149
+ return result
150
+ end
151
+
152
+ def before_destroy() end
153
+
154
+ def after_destroy() end
155
+ def destroy_with_callbacks #:nodoc:
156
+ return false if callback(:before_destroy) == false
157
+ result = destroy_without_callbacks
158
+ callback(:after_destroy)
159
+ result
160
+ end
161
+
162
+ private
163
+ def callback(method)
164
+ result = run_callbacks(method) { |result, object| false == result }
165
+
166
+ if result != false #&& respond_to_without_attributes?(method)
167
+ result = send(method)
168
+ end
169
+
170
+ notify(method)
171
+
172
+ return result
173
+ end
174
+
175
+ def notify(method) #:nodoc:
176
+ self.class.changed
177
+ self.class.notify_observers(method, self)
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,180 @@
1
+ require 'singleton'
2
+ require 'set'
3
+
4
+ module ActiveObject
5
+ module Observing # :nodoc:
6
+ def self.included(base)
7
+ base.extend ClassMethods
8
+ end
9
+
10
+ module ClassMethods
11
+ # 激活观察器. 示例:
12
+ #
13
+ # # 调用PersonObserver.instance
14
+ # ActiveObject::Base.observers = :person_observer
15
+ #
16
+ # # 调用Cacher.instance 和 GarbageCollector.instance
17
+ # ActiveObject::Base.observers = :cacher, :garbage_collector
18
+ #
19
+ # # 同上
20
+ # ActiveObject::Base.observers = Cacher, GarbageCollector
21
+ #
22
+ # Note: Setting this does not instantiate the observers yet. +instantiate_observers+ is
23
+ # called during startup, and before each development request.
24
+ def observers=(*observers)
25
+ @observers = observers.flatten
26
+ end
27
+
28
+ # 获得当前observers.
29
+ def observers
30
+ @observers ||= []
31
+ end
32
+
33
+ # 实例化全部Ant Mapper观察器.
34
+ def instantiate_observers
35
+ return if @observers.blank?
36
+ @observers.each do |observer|
37
+ if observer.respond_to?(:to_sym) # Symbol or String
38
+ observer.to_s.camelize.constantize.instance
39
+ elsif observer.respond_to?(:instance)
40
+ observer.instance
41
+ else
42
+ raise ArgumentError, "#{observer} must be a lowercase, underscored class name (or an instance of the class itself) responding to the instance method. Example: Person.observers = :big_brother # calls BigBrother.instance"
43
+ end
44
+ end
45
+ end
46
+
47
+ protected
48
+ # Notify observers when the observed class is subclassed.
49
+ def inherited(subclass)
50
+ super
51
+ changed
52
+ notify_observers :observed_class_inherited, subclass
53
+ end
54
+ end
55
+ end
56
+
57
+ # Observer classes respond to lifecycle callbacks to implement trigger-like
58
+ # behavior outside the original class. This is a great way to reduce the
59
+ # clutter that normally comes when the model class is burdened with
60
+ # functionality that doesn't pertain to the core responsibility of the
61
+ # class. Example:
62
+ #
63
+ # class CommentObserver < ActiveObject::Observer
64
+ # def after_save(comment)
65
+ # Notifications.deliver_comment("admin@do.com", "New comment was posted", comment)
66
+ # end
67
+ # end
68
+ #
69
+ # This Observer sends an email when a Comment#save is finished.
70
+ #
71
+ # class ContactObserver < ActiveObject::Observer
72
+ # def after_create(contact)
73
+ # contact.logger.info('New contact added!')
74
+ # end
75
+ #
76
+ # def after_destroy(contact)
77
+ # contact.logger.warn("Contact with an id of #{contact.id} was destroyed!")
78
+ # end
79
+ # end
80
+ #
81
+ # This Observer uses logger to log when specific callbacks are triggered.
82
+ #
83
+ # == Observing a class that can't be inferred
84
+ #
85
+ # Observers will by default be mapped to the class with which they share a name. So CommentObserver will
86
+ # be tied to observing Comment, ProductManagerObserver to ProductManager, and so on. If you want to name your observer
87
+ # differently than the class you're interested in observing, you can use the Observer.observe class method which takes
88
+ # either the concrete class (Product) or a symbol for that class (:product):
89
+ #
90
+ # class AuditObserver < ActiveObject::Observer
91
+ # observe :account
92
+ #
93
+ # def after_update(account)
94
+ # AuditTrail.new(account, "UPDATED")
95
+ # end
96
+ # end
97
+ #
98
+ # If the audit observer needs to watch more than one kind of object, this can be specified with multiple arguments:
99
+ #
100
+ # class AuditObserver < ActiveObject::Observer
101
+ # observe :account, :balance
102
+ #
103
+ # def after_update(record)
104
+ # AuditTrail.new(record, "UPDATED")
105
+ # end
106
+ # end
107
+ #
108
+ # The AuditObserver will now act on both updates to Account and Balance by treating them both as records.
109
+ #
110
+ # == Available callback methods
111
+ #
112
+ # The observer can implement callback methods for each of the methods described in the Callbacks module.
113
+ #
114
+ #
115
+ # == Loading
116
+ #
117
+ # Observers register themselves in the model class they observe, since it is the class that
118
+ # notifies them of events when they occur. As a side-effect, when an observer is loaded its
119
+ # corresponding model class is loaded.
120
+ #
121
+ # If by any chance you are using observed models in the initialization you can still
122
+ # load their observers by calling <tt>ModelObserver.instance</tt> before. Observers are
123
+ # singletons and that call instantiates and registers them.
124
+ #
125
+ class Observer
126
+ include Singleton
127
+
128
+ class << self
129
+ # Attaches the observer to the supplied model classes.
130
+ def observe(*models)
131
+ models.flatten!
132
+ models.collect! { |model| model.is_a?(Symbol) ? model.to_s.camelize.constantize : model }
133
+ define_method(:observed_classes) { Set.new(models) }
134
+ end
135
+
136
+ # The class observed by default is inferred from the observer's class name:
137
+ # assert_equal Person, PersonObserver.observed_class
138
+ def observed_class
139
+ if observed_class_name = name[/(.*)Observer/, 1]
140
+ observed_class_name.constantize
141
+ else
142
+ nil
143
+ end
144
+ end
145
+ end
146
+
147
+ # Start observing the declared classes and their subclasses.
148
+ def initialize
149
+ Set.new(observed_classes + observed_subclasses).each { |klass| add_observer! klass }
150
+ end
151
+
152
+ # Send observed_method(object) if the method exists.
153
+ def update(observed_method, object) #:nodoc:
154
+ send(observed_method, object) if respond_to?(observed_method)
155
+ end
156
+
157
+ # Special method sent by the observed class when it is inherited.
158
+ # Passes the new subclass.
159
+ def observed_class_inherited(subclass) #:nodoc:
160
+ self.class.observe(observed_classes + [subclass])
161
+ add_observer!(subclass)
162
+ end
163
+
164
+ protected
165
+ def observed_classes
166
+ Set.new([self.class.observed_class].compact.flatten)
167
+ end
168
+
169
+ def observed_subclasses
170
+ observed_classes.sum([]) { |klass| klass.send(:subclasses) }
171
+ end
172
+
173
+ def add_observer!(klass)
174
+ klass.add_observer(self)
175
+ if respond_to?(:after_find) && !klass.method_defined?(:after_find)
176
+ klass.class_eval 'def after_find() end'
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,99 @@
1
+ module ActiveObject #:nodoc:
2
+ module Serialization
3
+ class Serializer #:nodoc:
4
+ attr_reader :options
5
+
6
+ def initialize(object, options = {})
7
+ @object, @options = object, options.dup
8
+ end
9
+
10
+ # To replicate the behavior in ActiveObject#attributes,
11
+ # <tt>:except</tt> takes precedence over <tt>:only</tt>. If <tt>:only</tt> is not set
12
+ # for a N level model but is set for the N+1 level models,
13
+ # then because <tt>:except</tt> is set to a default value, the second
14
+ # level model can have both <tt>:except</tt> and <tt>:only</tt> set. So if
15
+ # <tt>:only</tt> is set, always delete <tt>:except</tt>.
16
+ def serializable_attribute_names
17
+ attribute_names = @object.attributes
18
+
19
+ if options[:only]
20
+ options.delete(:except)
21
+ attribute_names = attribute_names & Array(options[:only]).collect { |n| n.to_s }
22
+ else
23
+ options[:except] = Array(options[:except])
24
+ attribute_names = attribute_names - options[:except].collect { |n| n.to_s }
25
+ end
26
+
27
+ attribute_names
28
+ end
29
+
30
+ def serializable_method_names
31
+ Array(options[:methods]).inject([]) do |method_attributes, name|
32
+ method_attributes << name if @object.respond_to?(name.to_s)
33
+ method_attributes
34
+ end
35
+ end
36
+
37
+ def serializable_names
38
+ serializable_attribute_names + serializable_method_names
39
+ end
40
+
41
+ # Add associations specified via the <tt>:includes</tt> option.
42
+ # Expects a block that takes as arguments:
43
+ # +association+ - name of the association
44
+ # +objects+ - the association object(s) to be serialized
45
+ # +opts+ - options for the association objects
46
+ def add_includes(&block)
47
+ if include_associations = options.delete(:include)
48
+ base_only_or_except = { :except => options[:except],
49
+ :only => options[:only] }
50
+
51
+ include_has_options = include_associations.is_a?(Hash)
52
+ associations = include_has_options ? include_associations.keys : Array(include_associations)
53
+
54
+ for association in associations
55
+ objects = @object.send association
56
+
57
+ objects = objects.objects if objects.is_a?(ActiveObject::Associations::HasManyAssociation::Collection)
58
+
59
+ unless objects.nil?
60
+ association_options = include_has_options ? include_associations[association] : base_only_or_except
61
+ opts = options.merge(association_options)
62
+ yield(association, objects, opts)
63
+ end
64
+ end
65
+
66
+ options[:include] = include_associations
67
+ end
68
+ end
69
+
70
+ def serializable_object
71
+ returning(serializable_object = {}) do
72
+ serializable_names.each { |name| serializable_object[name] = @object.send(name) }
73
+
74
+ add_includes do |association, objects, opts|
75
+ if objects.is_a?(Enumerable)
76
+ serializable_object[association] = objects.collect { |r| self.class.new(r, opts).serializable_object }
77
+ serializable_object.delete("#{association}_ids")
78
+ else
79
+ serializable_object[association] = self.class.new(objects, opts).serializable_object
80
+ serializable_object.delete("#{association}_id")
81
+ end
82
+
83
+ end
84
+ end
85
+ end
86
+
87
+ def serialize
88
+ # overwrite to implement
89
+ end
90
+
91
+ def to_s(&block)
92
+ serialize(&block)
93
+ end
94
+ end
95
+ end
96
+ end
97
+
98
+ require 'active_object/serializers/xml_serializer'
99
+ require 'active_object/serializers/json_serializer'
@@ -0,0 +1,75 @@
1
+ module ActiveObject #:nodoc:
2
+ module Serialization
3
+ def self.included(base)
4
+ base.cattr_accessor :include_root_in_json, :instance_writer => false
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ # Returns a JSON string representing the model. Some configuration is
9
+ # available through +options+.
10
+ #
11
+ # Without any +options+, the returned JSON string will include all
12
+ # the model's attributes. For example:
13
+ #
14
+ # konata = User.find(1)
15
+ # konata.to_json
16
+ # # => {"id": 1, "name": "Konata Izumi", "age": 16,
17
+ # "created_at": "2006/08/01", "awesome": true}
18
+ #
19
+ # The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the attributes
20
+ # included, and work similar to the +attributes+ method. For example:
21
+ #
22
+ # konata.to_json(:only => [ :id, :name ])
23
+ # # => {"id": 1, "name": "Konata Izumi"}
24
+ #
25
+ # konata.to_json(:except => [ :id, :created_at, :age ])
26
+ # # => {"name": "Konata Izumi", "awesome": true}
27
+ #
28
+ # To include any methods on the model, use <tt>:methods</tt>.
29
+ #
30
+ # konata.to_json(:methods => :permalink)
31
+ # # => {"id": 1, "name": "Konata Izumi", "age": 16,
32
+ # "created_at": "2006/08/01", "awesome": true,
33
+ # "permalink": "1-konata-izumi"}
34
+ #
35
+ # To include associations, use <tt>:include</tt>.
36
+ #
37
+ # konata.to_json(:include => :posts)
38
+ # # => {"id": 1, "name": "Konata Izumi", "age": 16,
39
+ # "created_at": "2006/08/01", "awesome": true,
40
+ # "posts": [{"id": 1, "author_id": 1, "title": "Welcome to the weblog"},
41
+ # {"id": 2, author_id: 1, "title": "So I was thinking"}]}
42
+ #
43
+ # 2nd level and higher order associations work as well:
44
+ #
45
+ # konata.to_json(:include => { :posts => {
46
+ # :include => { :comments => {
47
+ # :only => :body } },
48
+ # :only => :title } })
49
+ # # => {"id": 1, "name": "Konata Izumi", "age": 16,
50
+ # "created_at": "2006/08/01", "awesome": true,
51
+ # "posts": [{"comments": [{"body": "1st post!"}, {"body": "Second!"}],
52
+ # "title": "Welcome to the weblog"},
53
+ # {"comments": [{"body": "Don't think too hard"}],
54
+ # "title": "So I was thinking"}]}
55
+ def to_json(options = {})
56
+ if include_root_in_json
57
+ "{#{self.class.json_class_name}: #{JsonSerializer.new(self, options).to_s}}"
58
+ else
59
+ JsonSerializer.new(self, options).to_s
60
+ end
61
+ end
62
+
63
+ class JsonSerializer < ActiveObject::Serialization::Serializer #:nodoc:
64
+ def serialize
65
+ serializable_object.to_json
66
+ end
67
+ end
68
+
69
+ module ClassMethods
70
+ def json_class_name
71
+ @json_class_name ||= name.demodulize.underscore.inspect
72
+ end
73
+ end
74
+ end
75
+ end