active-orient 0.4 → 0.5

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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Gemfile +8 -3
  4. data/Guardfile +12 -4
  5. data/README.md +221 -201
  6. data/VERSION +1 -1
  7. data/active-orient.gemspec +3 -2
  8. data/bin/active-orient-console +35 -0
  9. data/config/boot.rb +84 -16
  10. data/config/config.yml +10 -0
  11. data/config/connect.yml +6 -2
  12. data/create_project +19 -0
  13. data/examples/books.rb +86 -39
  14. data/examples/createTime.rb +91 -0
  15. data/examples/streets.rb +85 -84
  16. data/examples/test_commands.rb +92 -0
  17. data/examples/test_commands_2.rb +54 -0
  18. data/examples/test_commands_3.rb +48 -0
  19. data/examples/test_commands_4.rb +28 -0
  20. data/examples/time_graph/Gemfile +21 -0
  21. data/examples/time_graph/Guardfile +26 -0
  22. data/examples/time_graph/README.md +129 -0
  23. data/examples/time_graph/bin/active-orient-console +35 -0
  24. data/examples/time_graph/config/boot.rb +119 -0
  25. data/examples/time_graph/config/config.yml +8 -0
  26. data/examples/time_graph/config/connect.yml +17 -0
  27. data/examples/time_graph/config/init_db.rb +59 -0
  28. data/examples/time_graph/createTime.rb +51 -0
  29. data/examples/time_graph/lib/createTime.rb +82 -0
  30. data/examples/time_graph/model/day_of.rb +3 -0
  31. data/examples/time_graph/model/e.rb +6 -0
  32. data/examples/time_graph/model/edge.rb +53 -0
  33. data/examples/time_graph/model/monat.rb +19 -0
  34. data/examples/time_graph/model/stunde.rb +16 -0
  35. data/examples/time_graph/model/tag.rb +29 -0
  36. data/examples/time_graph/model/time_base.rb +6 -0
  37. data/examples/time_graph/model/time_of.rb +4 -0
  38. data/examples/time_graph/model/v.rb +3 -0
  39. data/examples/time_graph/model/vertex.rb +32 -0
  40. data/examples/time_graph/spec/lib/create_time_spec.rb +50 -0
  41. data/examples/time_graph/spec/rest_helper.rb +37 -0
  42. data/examples/time_graph/spec/spec_helper.rb +46 -0
  43. data/lib/active-orient.rb +56 -6
  44. data/lib/base.rb +149 -147
  45. data/lib/base_properties.rb +40 -41
  46. data/lib/class_utils.rb +301 -0
  47. data/lib/database_utils.rb +97 -0
  48. data/lib/init.rb +35 -0
  49. data/lib/java-api.rb +437 -0
  50. data/lib/jdbc.rb +211 -0
  51. data/lib/model/edge.rb +53 -0
  52. data/lib/model/model.rb +77 -0
  53. data/lib/model/the_class.rb +480 -0
  54. data/lib/model/the_record.rb +310 -0
  55. data/lib/model/vertex.rb +32 -0
  56. data/lib/orient.rb +113 -50
  57. data/lib/orientdb_private.rb +48 -0
  58. data/lib/other.rb +280 -0
  59. data/lib/query.rb +71 -73
  60. data/lib/rest/change.rb +124 -0
  61. data/lib/rest/create.rb +474 -0
  62. data/lib/rest/delete.rb +133 -0
  63. data/lib/rest/operations.rb +150 -0
  64. data/lib/rest/read.rb +150 -0
  65. data/lib/rest/rest.rb +111 -0
  66. data/lib/rest_disabled.rb +24 -0
  67. data/lib/support.rb +387 -296
  68. data/old_lib_functions/two_general_class.rb +139 -0
  69. data/usecase.md +49 -36
  70. data/usecase_oo.md +59 -0
  71. metadata +73 -9
  72. data/lib/model.rb +0 -461
  73. data/lib/rest.rb +0 -1036
  74. data/test.rb +0 -4
@@ -1,7 +1,8 @@
1
1
  module ActiveOrient
2
2
  require 'active_model'
3
- #
4
- # Base class for tableless IB data Models, extends ActiveModel API
3
+
4
+ # Base class for tableless IB data Models, extends ActiveModel API
5
+
5
6
  class Base
6
7
  extend ActiveModel::Naming
7
8
  extend ActiveModel::Callbacks
@@ -9,212 +10,213 @@ module ActiveOrient
9
10
  include ActiveModel::Serialization
10
11
  include ActiveModel::Serializers::Xml
11
12
  include ActiveModel::Serializers::JSON
12
-
13
+ include OrientDB
14
+
15
+ define_model_callbacks :initialize
13
16
 
14
- ##Every Rest::Base-Object is stored in the @@rid_store
15
- ## The Objects are just references to the @@rid_store.
16
- ## any Change of the Object is thus synchonized to any allocated variable
17
- #
18
- @@rid_store = Hash.new
17
+ # ActiveRecord::Base callback API mocks
18
+ define_model_callbacks :initialize, :only => :after
19
+ mattr_accessor :logger
19
20
 
20
- def self.display_riid
21
+ # Used to read the metadata
22
+ attr_reader :metadata
23
+
24
+ =begin
25
+ Every Rest::Base-Object is stored in the @@rid_store
26
+ The Objects are just references to the @@rid_store.
27
+ Any Change of the Object is thus synchonized to any allocated variable.
28
+ =end
29
+ @@rid_store = Hash.new
30
+
31
+ def self.display_rid
21
32
  @@rid_store
22
33
  end
23
- def self.remove_riid obj
24
- @@rid_store[obj.riid]=nil
34
+
35
+ def self.remove_rid obj
36
+ @@rid_store.delete obj.rid
25
37
  end
26
- def self.get_riid link
27
38
 
39
+ def self.get_rid rid
40
+ rid = rid[1..-1] if rid[0]=='#'
41
+ @@rid_store[rid]
28
42
  end
29
- def self.store_riid obj
30
- if obj.rid.present? && obj.riid.all?{|x| x.present? && x>=0} # only positive values are stored
31
- ## return the presence of a stored object as true by the block
32
- ## the block is only executed if the presence is confirmed
33
- ## Nothing is returned from the class-method
34
- if @@rid_store[obj.riid].present?
35
- yield if block_given?
36
- end
37
- @@rid_store[obj.riid] = obj
38
- @@rid_store[obj.riid] # return_value
43
+
44
+ def self.store_rid obj
45
+ if obj.rid.present? && obj.rid.rid?
46
+ # return the presence of a stored object as true by the block
47
+ # the block is only executed if the presence is confirmed
48
+ # Nothing is returned from the class-method
49
+ if @@rid_store[obj.rid].present?
50
+ yield if block_given?
51
+ end
52
+ @@rid_store[obj.rid] = obj
53
+ @@rid_store[obj.rid] # return_value
39
54
  else
40
- obj # no rid-value: just return the obj
55
+ obj # no rid-value: just return the obj
41
56
  end
42
57
  end
43
58
 
59
+ def document
60
+ @d
61
+ end
44
62
 
45
- define_model_callbacks :initialize
63
+ =begin
64
+ If a opts hash is given, keys are taken as attribute names, values as data.
65
+ The model instance fields are then set automatically from the opts Hash.
66
+ =end
46
67
 
47
- mattr_accessor :logger
48
- # If a opts hash is given, keys are taken as attribute names, values as data.
49
- # The model instance fields are then set automatically from the opts Hash.
50
- def initialize attributes={}, opts={}
51
- logger.progname= "ActiveOrient::Base#initialize"
52
- #possible_link_array_candidates = link_candidates = Hash.new
68
+ def initialize attributes = {}, opts = {}
69
+ logger.progname = "ActiveOrient::Base#initialize"
53
70
  @metadata = HashWithIndifferentAccess.new
54
- # @edges = HashWithIndifferentAccess.new
55
-
71
+ @d = nil
56
72
  run_callbacks :initialize do
57
- # puts "initialize::attributes: #{attributes.inspect}"
58
-
59
- attributes.keys.each do | att |
60
- unless att[0] == "@" # @ identifies Metadata-attributes
61
- att = att.to_sym if att.is_a?(String)
62
- unless self.class.instance_methods.detect{|x| x == att }
63
- self.class.define_property att, nil
64
- # logger.debug { "property #{att.to_s} assigned to #{self.class.to_s}" }
73
+ if RUBY_PLATFORM == 'java' && attributes.is_a?( Document )
74
+ @d = attributes
75
+ attributes = @d.values
76
+ @metadata[:class] = @d.class_name
77
+ @metadata[:version] = @d.version
78
+ @metadata[:cluster], @metadata[:record] = @d.rid[1,@d.rid.size].split(':')
79
+
80
+
81
+
82
+ end
83
+ attributes.keys.each do |att|
84
+ unless att[0] == "@" # @ identifies Metadata-attributes
85
+ att = att.to_sym if att.is_a?(String)
86
+ unless self.class.instance_methods.detect{|x| x == att}
87
+ self.class.define_property att, nil
65
88
  else
66
- # logger.info{ "property #{att.to_s} NOT assigned " }
89
+ #logger.info{"Property #{att.to_s} NOT assigned"}
67
90
  end
68
91
  end
69
92
  end
70
93
 
71
- if attributes['@type'] == 'd' # document
72
- @metadata[ :type ] = attributes.delete '@type'
73
- @metadata[ :class ] = attributes.delete '@class'
74
- @metadata[ :version ] = attributes.delete '@version'
75
- @metadata[ :fieldTypes ] = attributes.delete '@fieldTypes'
76
- if attributes.has_key?( '@rid' )
77
- rid = attributes.delete '@rid'
78
- cluster, record = rid[1,rid.size].split(':')
79
- @metadata[ :cluster ] = cluster.to_i
80
- @metadata[ :record ] = record.to_i
94
+ if attributes['@type'] == 'd' # document via REST
95
+ @metadata[:type] = attributes.delete '@type'
96
+ @metadata[:class] = attributes.delete '@class'
97
+ @metadata[:version] = attributes.delete '@version'
98
+ @metadata[:fieldTypes] = attributes.delete '@fieldTypes'
99
+ if attributes.has_key?('@rid')
100
+ rid = attributes.delete '@rid'
101
+ cluster, record = rid[1,rid.size].split(':')
102
+ @metadata[:cluster] = cluster.to_i
103
+ @metadata[:record] = record.to_i
81
104
  end
82
105
 
83
- #### edges -- remove in_ and out_ and de-capitalize the remaining edge
84
- if @metadata[ :fieldTypes ].present? && (@metadata[ :fieldTypes ] =~ /=g/)
106
+ if @metadata[:fieldTypes ].present? && (@metadata[:fieldTypes] =~ /=g/)
85
107
  edges = @metadata['fieldTypes'].split(',').find_all{|x| x=~/=g/}.map{|x| x.split('=').first}
86
108
  edges.each do |edge|
87
- operator, *base_edge = edge.split('_')
109
+ operator, *base_edge = edge.split('_')
88
110
  base_edge = base_edge.join('_')
89
- unless self.class.instance_methods.detect{|x| x == base_edge }
90
- ## define two methods: out_{Edge}/{in_Edge} -> edge.
91
- self.class.define_property base_edge, nil
92
- self.class.send :alias_method, base_edge.underscore, edge #
93
- # logger.debug { "#{link}:: edge #{edge} assigned to #{self.class.to_s} and remaped to #{base_edge.underscore}" }
94
-
111
+ unless self.class.instance_methods.detect{|x| x == base_edge}
112
+ ## define two methods: out_{Edge}/in_{Edge} -> edge.
113
+ self.class.define_property base_edge, nil
114
+ self.class.send :alias_method, base_edge.underscore, edge
95
115
  end
96
116
  end
97
117
  end
98
118
  end
99
-
100
-
101
- self.attributes = attributes # set_attribute_defaults is now after_init callback
102
- end
103
- ActiveOrient::Base.store_riid self
119
+ self.attributes = attributes # set_attribute_defaults is now after_init callback
104
120
  end
121
+ # puts "Storing #{self.rid} to rid-store"
122
+ ActiveOrient::Base.store_rid self
123
+ end
105
124
 
106
- # ActiveModel API (for serialization)
125
+ # ActiveModel API (for serialization)
107
126
 
108
127
  def attributes
109
128
  @attributes ||= HashWithIndifferentAccess.new
110
129
  end
111
130
 
112
131
  def attributes= attrs
113
- attrs.keys.each { |key| self.send("#{key}=", attrs[key]) }
132
+ attrs.keys.each{|key| self.send("#{key}=", attrs[key])}
114
133
  end
115
134
 
116
- # ActiveModel-style read/write_attribute accessors
117
- # Here we define the autoload mechanism
118
- def [] key
135
+ =begin
136
+ ActiveModel-style read/write_attribute accessors
137
+ Autoload mechanism and data conversion are defined in the method "from_orient" of each class
138
+ =end
119
139
 
120
- iv= attributes[key.to_sym]
121
- # iv.from_orient unless iv.nil?
122
- if iv.is_a?(String) && iv.rid? #&& @metadata[:fieldTypes].present? && @metadata[:fieldTypes].include?( key.to_s+"=x" )
123
- # puts "autoload: #{iv}"
124
- ActiveOrient::Model.autoload_object iv
125
- elsif iv.is_a?(Array) # && @metadata[:fieldTypes].present? && @metadata[:fieldTypes].match( key.to_s+"=[znmgx]" )
126
- # puts "autoload: #{iv.inspect}"
127
- OrientSupport::Array.new self, *iv.map{|y| (y.is_a?(String) && y.rid?) ? ActiveOrient::Model.autoload_object( y ) : y }
140
+ def [] key
141
+ iv = attributes[key.to_sym]
142
+ if @metadata[:fieldTypes].present? && @metadata[:fieldTypes].include?(key.to_s+"=t")
143
+ iv =~ /00:00:00/ ? Date.parse(iv) : DateTime.parse(iv)
144
+ elsif iv.is_a? Array
145
+ OrientSupport::Array.new( work_on: self, work_with: iv.from_orient){ key.to_sym }
146
+ elsif iv.is_a? Hash
147
+ OrientSupport::Hash.new( self, iv){ key.to_sym }
148
+ # elsif iv.is_a? RecordMap
149
+ # iv
150
+ # puts "RecordSet detected"
128
151
  else
129
-
130
- iv
152
+ iv.from_orient
131
153
  end
132
154
  end
133
155
 
134
- def update_attribute key, value
135
- @attributes[key] = value
136
- end
137
- =begin
138
- Here we define how the attributes are initialized
139
- Key and val are set by the RestCliend
140
- =end
141
156
  def []= key, val
142
- val = val.rid if val.is_a? ActiveOrient::Model
143
- # if val.is_a?(Array) # && @metadata[:fieldTypes].present? && @metadata[:fieldTypes].include?( key.to_s+"=n" )
144
- # if @metadata[ :fieldTypes ] =~ /out=x,in=x/
145
- # puts "VAL is a ARRAY"
146
- # else
147
- # puts "METADATA: #{ @metadata[ :fieldTypes ]} "
148
- # end
149
- # val# = val.map{|x| if val.is_a? ActiveOrient::Model then val.rid else val end }
150
- # end
157
+ val = val.rid if val.is_a?( ActiveOrient::Model ) && val.rid.rid?
151
158
  attributes[key.to_sym] = case val
152
- when Array
153
- if val.first.is_a?(Hash)
154
- v=val.map do |x|
155
- if x.is_a?( Hash )
156
- HashWithIndifferentAccess.new(x)
157
- else
158
- x
159
- end
160
- end
161
- OrientSupport::Array.new( self, *v )
162
- else
163
- OrientSupport::Array.new( self, *val )
164
- end
165
- when Hash
166
- HashWithIndifferentAccess.new(val)
167
- else
168
- val
169
- end
170
-
159
+ when Array
160
+ if val.first.is_a?(Hash)
161
+ v = val.map do |x|
162
+ if x.is_a?(Hash)
163
+ HashWithIndifferentAccess.new(x)
164
+ else
165
+ x
166
+ end
167
+ end
168
+ OrientSupport::Array.new(work_on: self, work_with: v )
169
+ else
170
+ OrientSupport::Array.new(work_on: self, work_with: val )
171
+ end
172
+ when Hash
173
+ HashWithIndifferentAccess.new(val)
174
+ else
175
+ val
176
+ end
177
+ end
178
+
179
+ def update_attribute key, value
180
+ @attributes[key] = value
171
181
  end
172
182
 
173
183
  def to_model
174
184
  self
175
185
  end
176
186
 
187
+ # Noop methods mocking ActiveRecord::Base macros
177
188
 
178
-
179
- ### Noop methods mocking ActiveRecord::Base macros
180
-
181
189
  def self.attr_protected *args
182
190
  end
183
191
 
184
192
  def self.attr_accessible *args
185
193
  end
186
194
 
187
- ### ActiveRecord::Base association API mocks
188
-
189
- def self.belongs_to model, *args
190
- attr_accessor model
191
- end
192
-
193
- def self.has_one model, *args
194
- attr_accessor model
195
- end
196
-
197
- def self.has_many models, *args
198
- attr_accessor models
199
-
200
- define_method(models) do
201
- self.instance_variable_get("@#{models}") ||
202
- self.instance_variable_set("@#{models}", [])
203
- end
204
- end
205
-
206
- def self.find *args
207
- []
208
- end
209
-
210
- ### ActiveRecord::Base callback API mocks
211
-
212
- define_model_callbacks :initialize, :only => :after
213
-
214
- ### ActiveRecord::Base misc
195
+ # ActiveRecord::Base association API mocks
196
+ #
197
+ # def self.belongs_to model, *args
198
+ # attr_accessor model
199
+ # end
200
+ #
201
+ # def self.has_one model, *args
202
+ # attr_accessor model
203
+ # end
204
+ #
205
+ # def self.has_many models, *args
206
+ # attr_accessor models
207
+ # define_method(models) do
208
+ # self.instance_variable_get("@#{models}") || self.instance_variable_set("@#{models}", [])
209
+ # end
210
+ # end
211
+ #
212
+ # def self.find *args
213
+ # []
214
+ # end
215
+ #
216
+ # ActiveRecord::Base misc
215
217
 
216
218
  def self.serialize *properties
217
219
  end
218
220
 
219
221
  end # Model
220
- end # module
222
+ end # module
@@ -3,83 +3,83 @@ require 'active_support/concern'
3
3
  require 'active_support/hash_with_indifferent_access'
4
4
 
5
5
  module ActiveOrient
6
-
7
- # Module adds prop Macro and
8
6
  module BaseProperties
9
7
  extend ActiveSupport::Concern
10
8
 
11
- ### Instance methods
9
+ # Default presentation of ActiveOrient::Model-Objects
12
10
 
13
- # Default presentation
14
11
  def to_human
15
12
  "<#{self.class.to_s.demodulize}: " + content_attributes.map do |attr, value|
16
- "#{attr}: #{value}" unless value.nil?
17
- end.compact.sort.join(' ') + ">"
13
+ v= case value
14
+ when ActiveOrient::Model
15
+ "< #{self.class.to_.demodulize} : #{value.rrid} >"
16
+ else
17
+ value.from_orient
18
+ end
19
+ "%s : %s" % [ attr, v] unless v.nil?
20
+ end.compact.sort.join(', ') + ">".gsub('"' , ' ')
18
21
  end
19
22
 
20
- # Comparison support
21
- def content_attributes
23
+ # Comparison support
24
+
25
+ def content_attributes # :nodoc:
22
26
  HashWithIndifferentAccess[attributes.reject do |(attr, _)|
23
- attr.to_s =~ /(_count)\z/ ||
24
- [:created_at, :updated_at, :type,
25
- :id, :order_id, :contract_id].include?(attr.to_sym)
27
+ attr.to_s =~ /(_count)\z/ || [:created_at, :updated_at, :type, :id, :order_id, :contract_id].include?(attr.to_sym)
26
28
  end]
27
29
  end
28
30
 
29
- # Update nil attributes from given Hash or model
30
- def update_missing attrs
31
- attrs = attrs.content_attributes unless attrs.kind_of?(Hash)
31
+ # Update nil attributes from given Hash or model
32
32
 
33
- attrs.each { |attr, val| send "#{attr}=", val if send(attr).blank? }
33
+ def update_missing attrs # :nodoc:
34
+ attrs = attrs.content_attributes unless attrs.kind_of?(Hash)
35
+ attrs.each{|attr, val| send "#{attr}=", val if send(attr).blank?}
34
36
  self # for chaining
35
37
  end
36
38
 
37
- # Default Model comparison
38
- def == other
39
+ # Default Model comparison
40
+
41
+ def == other # :nodoc:
39
42
  case other
40
43
  when String # Probably a link or a rid
41
- link == other || rid == other
44
+ "##{rid}" == other || rid == other
45
+ when ActiveOrient::Model
46
+ rid == other.rid
42
47
  else
43
- content_attributes.keys.inject(true) { |res, key|
44
- res && other.respond_to?(key) && (send(key) == other.send(key)) }
48
+ content_attributes.keys.inject(true){ |res, key|
49
+ res && other.respond_to?(key) && (send(key) == other.send(key))
50
+ }
45
51
  end
46
52
  end
47
53
 
48
- ### Default attributes support
54
+ # Default attributes support
49
55
 
50
56
  def default_attributes
51
57
  {:created_at => Time.now,
52
- :updated_at => Time.now,
53
- }
58
+ :updated_at => Time.now}
54
59
  end
55
60
 
56
- def set_attribute_defaults
61
+ def set_attribute_defaults # :nodoc:
57
62
  default_attributes.each do |key, val|
58
63
  self.send("#{key}=", val) if self.send(key).nil?
59
- # self.send("#{key}=", val) if self[key].nil? # Problems with association defaults
60
64
  end
61
65
  end
62
66
 
63
67
  included do
64
-
65
68
  after_initialize :set_attribute_defaults
66
69
 
67
- ### Class macros
70
+ # Class macros
68
71
 
69
- def self.prop *properties
72
+ def self.prop *properties # :nodoc:
70
73
  prop_hash = properties.last.is_a?(Hash) ? properties.pop : {}
71
-
72
74
  properties.each { |names| define_property names, nil }
73
75
  prop_hash.each { |names, type| define_property names, type }
74
76
  end
75
77
 
76
- def self.define_property names, body
78
+ def self.define_property names, body # :nodoc:
77
79
  aliases = [names].flatten
78
80
  name = aliases.shift
79
81
  instance_eval do
80
-
81
82
  define_property_methods name, body
82
-
83
83
  aliases.each do |ali|
84
84
  alias_method "#{ali}", name
85
85
  alias_method "#{ali}=", "#{name}="
@@ -87,8 +87,7 @@ module ActiveOrient
87
87
  end
88
88
  end
89
89
 
90
- def self.define_property_methods name, body={}
91
- #p name, body
90
+ def self.define_property_methods name, body={} # :nodoc:
92
91
  case body
93
92
  when '' # default getter and setter
94
93
  define_property_methods name
@@ -100,23 +99,24 @@ module ActiveOrient
100
99
  :validate => body[2]
101
100
 
102
101
  when Hash # recursion base case
102
+ # puts "NAME: "+name.to_S
103
+ # puts "BODY::"+body.inspect
103
104
  getter = case # Define getter
104
105
  when body[:get].respond_to?(:call)
105
106
  body[:get]
106
107
  when body[:get]
107
- proc { self[name].send "to_#{body[:get]}" }
108
+ proc{self[name].send "to_#{body[:get]}"}
108
109
  else
109
- proc { self[name] }
110
+ proc{self[name]}
110
111
  end
111
112
  define_method name, &getter if getter
112
-
113
113
  setter = case # Define setter
114
114
  when body[:set].respond_to?(:call)
115
115
  body[:set]
116
116
  when body[:set]
117
- proc { |value| self[name] = value.send "to_#{body[:set]}" }
117
+ proc{|value| self[name] = value.send "to_#{body[:set]}"}
118
118
  else
119
- proc { |value| self[name] = value } # p name, value;
119
+ proc{|value| self[name] = value} # p name, value;
120
120
  end
121
121
  define_method "#{name}=", &setter if setter
122
122
 
@@ -130,14 +130,13 @@ module ActiveOrient
130
130
  end
131
131
  end
132
132
 
133
- # TODO define self[:name] accessors for :virtual and :flag properties
133
+ # todo define self[:name] accessors for :virtual and :flag properties
134
134
 
135
135
  else # setter given
136
136
  define_property_methods name, :set => body, :get => body
137
137
  end
138
138
  end
139
139
 
140
- # Timestamps in lightweight models
141
140
  unless defined?(ActiveRecord::Base) && ancestors.include?(ActiveRecord::Base)
142
141
  prop :created_at, :updated_at
143
142
  end