datastax_rails 2.0.3 → 2.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6d858bc7834309a95814f407b2c85512b50cfa7a
4
- data.tar.gz: c8c2896838848cd1f00a9c8f662958682ec60cff
3
+ metadata.gz: 06972ed110ef94f1e39ad303f9d83b3c012f2840
4
+ data.tar.gz: 633ea7c7ff603672a07bc70ecb5b62059d12d18d
5
5
  SHA512:
6
- metadata.gz: b9cd869dd42101301ad8450e10e45bb983628217a079831fc834e0d65652bd87212ac8ecd90455c7c40fc1632a099f682a30786d42686fd9c4bbd8c64a312580
7
- data.tar.gz: 44a0baf08768ac8147fc59555c6cacc3021c95c0da3138aeb0c09790bf28fbcd0ee506faee060a264e4e23be5c5aa97a8ecd1e65a6c20d98ecb9a66b0d9325c6
6
+ metadata.gz: b826619dcf22f443352173c26accb3f994a8b229739aa6ab71e64013a05fed9df1d59af3478f05bbcb21dc19637699ad009dc5c577abf1c87fe2984d29ee05db
7
+ data.tar.gz: f86a2dec43ce66e55c270b494554b15d4a33ace9e84481d459e5200f32a71c5045575befe6549a4147f04288ac6a28ff8c36ccf4fc256e4046a5bd70997ff180
@@ -1,4 +1,256 @@
1
1
  module DatastaxRails
2
+ # = Datastax Rails Callbacks
3
+ #
4
+ # Callbacks are hooks into the life cycle of an Datastax Rails object that allow you to trigger logic
5
+ # before or after an alteration of the object state. This can be used to make sure that associated and
6
+ # dependent objects are deleted when +destroy+ is called (by overwriting +before_destroy+) or to massage attributes
7
+ # before they're validated (by overwriting +before_validation+). As an example of the callbacks initiated, consider
8
+ # the <tt>Base#save</tt> call for a new record:
9
+ #
10
+ # * (-) <tt>save</tt>
11
+ # * (-) <tt>valid</tt>
12
+ # * (1) <tt>before_validation</tt>
13
+ # * (-) <tt>validate</tt>
14
+ # * (2) <tt>after_validation</tt>
15
+ # * (3) <tt>before_save</tt>
16
+ # * (4) <tt>before_create</tt>
17
+ # * (-) <tt>create</tt>
18
+ # * (5) <tt>after_create</tt>
19
+ # * (6) <tt>after_save</tt>
20
+ # * (7) <tt>after_commit</tt>
21
+ #
22
+ # Additionally, an <tt>after_touch</tt> callback is triggered whenever an
23
+ # object is touched.
24
+ #
25
+ # Lastly an <tt>after_find</tt> and <tt>after_initialize</tt> callback is triggered for each object that
26
+ # is found and instantiated by a finder, with <tt>after_initialize</tt> being triggered after new objects
27
+ # are instantiated as well.
28
+ #
29
+ # There are eighteen callbacks in total, which give you immense power to react and prepare for each state in the
30
+ # Datastax Rails life cycle. The sequence for calling <tt>Base#save</tt> for an existing record is similar,
31
+ # except that each <tt>_create</tt> callback is replaced by the corresponding <tt>_update</tt> callback.
32
+ #
33
+ # Examples:
34
+ # class CreditCard < DatastaxRails::Base
35
+ # # Strip everything but digits, so the user can specify "555 234 34" or
36
+ # # "5552-3434" and both will mean "55523434"
37
+ # before_validation(on: :create) do
38
+ # self.number = number.gsub(/[^0-9]/, "") if attribute_present?("number")
39
+ # end
40
+ # end
41
+ #
42
+ # class Subscription < DatastaxRails::Base
43
+ # before_create :record_signup
44
+ #
45
+ # private
46
+ # def record_signup
47
+ # self.signed_up_on = Date.today
48
+ # end
49
+ # end
50
+ #
51
+ # class Firm < DatastaxRails::Base
52
+ # # Destroys the associated clients and people when the firm is destroyed
53
+ # before_destroy { |record| Person.destroy_all "firm_id = #{record.id}" }
54
+ # before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
55
+ # end
56
+ #
57
+ # == Inheritable callback queues
58
+ #
59
+ # Besides the overwritable callback methods, it's also possible to register callbacks through the
60
+ # use of the callback macros. Their main advantage is that the macros add behavior into a callback
61
+ # queue that is kept intact down through an inheritance hierarchy.
62
+ #
63
+ # class Topic < DatastaxRails::Base
64
+ # before_destroy :destroy_author
65
+ # end
66
+ #
67
+ # class Reply < Topic
68
+ # before_destroy :destroy_readers
69
+ # end
70
+ #
71
+ # Now, when <tt>Topic#destroy</tt> is run only +destroy_author+ is called. When <tt>Reply#destroy</tt> is
72
+ # run, both +destroy_author+ and +destroy_readers+ are called. Contrast this to the following situation
73
+ # where the +before_destroy+ method is overridden:
74
+ #
75
+ # class Topic < DatastaxRails::Base
76
+ # def before_destroy() destroy_author end
77
+ # end
78
+ #
79
+ # class Reply < Topic
80
+ # def before_destroy() destroy_readers end
81
+ # end
82
+ #
83
+ # In that case, <tt>Reply#destroy</tt> would only run +destroy_readers+ and _not_ +destroy_author+.
84
+ # So, use the callback macros when you want to ensure that a certain callback is called for the entire
85
+ # hierarchy, and use the regular overwritable methods when you want to leave it up to each descendant
86
+ # to decide whether they want to call +super+ and trigger the inherited callbacks.
87
+ #
88
+ # *IMPORTANT:* In order for inheritance to work for the callback queues, you must specify the
89
+ # callbacks before specifying the associations. Otherwise, you might trigger the loading of a
90
+ # child before the parent has registered the callbacks and they won't be inherited.
91
+ #
92
+ # == Types of callbacks
93
+ #
94
+ # There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
95
+ # inline methods (using a proc), and inline eval methods (using a string). Method references and callback objects
96
+ # are the recommended approaches, inline methods using a proc are sometimes appropriate (such as for
97
+ # creating mix-ins), and inline eval methods are deprecated.
98
+ #
99
+ # The method reference callbacks work by specifying a protected or private method available in the object, like this:
100
+ #
101
+ # class Topic < DatastaxRails::Base
102
+ # before_destroy :delete_parents
103
+ #
104
+ # private
105
+ # def delete_parents
106
+ # self.class.delete_all "parent_id = #{id}"
107
+ # end
108
+ # end
109
+ #
110
+ # The callback objects have methods named after the callback called with the record as the only parameter, such as:
111
+ #
112
+ # class BankAccount < DatastaxRails::Base
113
+ # before_save EncryptionWrapper.new
114
+ # after_save EncryptionWrapper.new
115
+ # after_initialize EncryptionWrapper.new
116
+ # end
117
+ #
118
+ # class EncryptionWrapper
119
+ # def before_save(record)
120
+ # record.credit_card_number = encrypt(record.credit_card_number)
121
+ # end
122
+ #
123
+ # def after_save(record)
124
+ # record.credit_card_number = decrypt(record.credit_card_number)
125
+ # end
126
+ #
127
+ # alias_method :after_initialize, :after_save
128
+ #
129
+ # private
130
+ # def encrypt(value)
131
+ # # Secrecy is committed
132
+ # end
133
+ #
134
+ # def decrypt(value)
135
+ # # Secrecy is unveiled
136
+ # end
137
+ # end
138
+ #
139
+ # So you specify the object you want messaged on a given callback. When that callback is triggered, the object has
140
+ # a method by the name of the callback messaged. You can make these callbacks more flexible by passing in other
141
+ # initialization data such as the name of the attribute to work with:
142
+ #
143
+ # class BankAccount < DatastaxRails::Base
144
+ # before_save EncryptionWrapper.new("credit_card_number")
145
+ # after_save EncryptionWrapper.new("credit_card_number")
146
+ # after_initialize EncryptionWrapper.new("credit_card_number")
147
+ # end
148
+ #
149
+ # class EncryptionWrapper
150
+ # def initialize(attribute)
151
+ # @attribute = attribute
152
+ # end
153
+ #
154
+ # def before_save(record)
155
+ # record.send("#{@attribute}=", encrypt(record.send("#{@attribute}")))
156
+ # end
157
+ #
158
+ # def after_save(record)
159
+ # record.send("#{@attribute}=", decrypt(record.send("#{@attribute}")))
160
+ # end
161
+ #
162
+ # alias_method :after_initialize, :after_save
163
+ #
164
+ # private
165
+ # def encrypt(value)
166
+ # # Secrecy is committed
167
+ # end
168
+ #
169
+ # def decrypt(value)
170
+ # # Secrecy is unveiled
171
+ # end
172
+ # end
173
+ #
174
+ # The callback macros usually accept a symbol for the method they're supposed to run, but you can also
175
+ # pass a "method string", which will then be evaluated within the binding of the callback. Example:
176
+ #
177
+ # class Topic < DatastaxRails::Base
178
+ # before_destroy 'self.class.delete_all "parent_id = #{id}"'
179
+ # end
180
+ #
181
+ # Notice that single quotes (') are used so the <tt>#{id}</tt> part isn't evaluated until the callback
182
+ # is triggered. Also note that these inline callbacks can be stacked just like the regular ones:
183
+ #
184
+ # class Topic < DatastaxRails::Base
185
+ # before_destroy 'self.class.delete_all "parent_id = #{id}"',
186
+ # 'puts "Evaluated after parents are destroyed"'
187
+ # end
188
+ #
189
+ # == <tt>before_validation*</tt> returning statements
190
+ #
191
+ # If the returning value of a +before_validation+ callback can be evaluated to +false+, the process will be
192
+ # aborted and <tt>Base#save</tt> will return +false+. If Base#save! is called it will raise a
193
+ # DatastaxRails::RecordInvalid exception. Nothing will be appended to the errors object.
194
+ #
195
+ # == Canceling callbacks
196
+ #
197
+ # If a <tt>before_*</tt> callback returns +false+, all the later callbacks and the associated action are
198
+ # cancelled. If an <tt>after_*</tt> callback returns +false+, all the later callbacks are cancelled.
199
+ # Callbacks are generally run in the order they are defined, with the exception of callbacks defined as
200
+ # methods on the model, which are called last.
201
+ #
202
+ # == Ordering callbacks
203
+ #
204
+ # Sometimes the code needs that the callbacks execute in a specific order. For example, a +before_destroy+
205
+ # callback (+log_children+ in this case) should be executed before the children get destroyed by the +dependent: destroy+ option.
206
+ #
207
+ # Let's look at the code below:
208
+ #
209
+ # class Topic < DatastaxRails::Base
210
+ # has_many :children, dependent: destroy
211
+ #
212
+ # before_destroy :log_children
213
+ #
214
+ # private
215
+ # def log_children
216
+ # # Child processing
217
+ # end
218
+ # end
219
+ #
220
+ # In this case, the problem is that when the +before_destroy+ callback is executed, the children are not available
221
+ # because the +destroy+ callback gets executed first. You can use the +prepend+ option on the +before_destroy+ callback to avoid this.
222
+ #
223
+ # class Topic < DatastaxRails::Base
224
+ # has_many :children, dependent: destroy
225
+ #
226
+ # before_destroy :log_children, prepend: true
227
+ #
228
+ # private
229
+ # def log_children
230
+ # # Child processing
231
+ # end
232
+ # end
233
+ #
234
+ # This way, the +before_destroy+ gets executed before the <tt>dependent: destroy</tt> is called, and the data is still available.
235
+ #
236
+ # == Debugging callbacks
237
+ #
238
+ # The callback chain is accessible via the <tt>_*_callbacks</tt> method on an object. ActiveModel Callbacks support
239
+ # <tt>:before</tt>, <tt>:after</tt> and <tt>:around</tt> as values for the <tt>kind</tt> property. The <tt>kind</tt> property
240
+ # defines what part of the chain the callback runs in.
241
+ #
242
+ # To find all callbacks in the before_save callback chain:
243
+ #
244
+ # Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }
245
+ #
246
+ # Returns an array of callback objects that form the before_save chain.
247
+ #
248
+ # To further check if the before_save chain contains a proc defined as <tt>rest_when_dead</tt> use the <tt>filter</tt> property of the callback object:
249
+ #
250
+ # Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }.collect(&:filter).include?(:rest_when_dead)
251
+ #
252
+ # Returns true or false depending on whether the proc is contained in the before_save callback chain on a Topic model.
253
+ #
2
254
  module Callbacks
3
255
  extend ActiveSupport::Concern
4
256
 
@@ -9,33 +261,37 @@ module DatastaxRails
9
261
  :before_destroy, :around_destroy, :after_destroy, :after_commit, :after_rollback
10
262
  ]
11
263
 
264
+ module ClassMethods
265
+ include ActiveModel::Callbacks
266
+ end
12
267
 
13
268
  included do
14
- extend ActiveModel::Callbacks
15
269
  include ActiveModel::Validations::Callbacks
270
+
16
271
  define_model_callbacks :initialize, :find, :touch, :only => :after
17
272
  define_model_callbacks :save, :create, :update, :destroy
18
273
  end
19
274
 
20
275
  def destroy(*) #:nodoc:
21
- #_run_destroy_callbacks { super }
22
276
  run_callbacks(:destroy) { super }
23
277
  end
24
278
 
25
- private
26
- def _create_or_update(*) #:nodoc:
27
- #_run_save_callbacks { super }
28
- run_callbacks(:save) { super }
29
- end
30
-
31
- def _create(*) #:nodoc:
32
- #_run_create_callbacks { super }
33
- run_callbacks(:create) { super }
34
- end
35
-
36
- def _update(*) #:nodoc:
37
- #_run_update_callbacks { super }
38
- run_callbacks(:update) { super }
39
- end
279
+ def touch(*) #:nodoc:
280
+ run_callbacks(:touch) { super }
281
+ end
282
+
283
+ private
284
+
285
+ def _create_or_update(*) #:nodoc:
286
+ run_callbacks(:save) { super }
287
+ end
288
+
289
+ def _create_record(*) #:nodoc:
290
+ run_callbacks(:create) { super }
291
+ end
292
+
293
+ def _update_record(*) #:nodoc:
294
+ run_callbacks(:update) { super }
295
+ end
40
296
  end
41
- end
297
+ end
@@ -146,10 +146,11 @@ module DatastaxRails
146
146
  return coder.dump(value) if encoded?
147
147
 
148
148
  case (dest_type || type)
149
- when :uuid then value.is_a?(::Cql::Uuid) ? value : self.class.value_to_uuid(value)
150
- when :date then value.to_time
151
- when :list, :set then list_to_cql3_value(value)
152
- when :map then map_to_cql3_value(value)
149
+ when :uuid then value.is_a?(::Cql::Uuid) ? value : self.class.value_to_uuid(value)
150
+ when :time, :datetime, :timestamp then value.to_time.utc
151
+ when :date then value.to_time.utc
152
+ when :list, :set then list_to_cql3_value(value)
153
+ when :map then map_to_cql3_value(value)
153
154
  else value
154
155
  end
155
156
  end
@@ -163,7 +164,7 @@ module DatastaxRails
163
164
 
164
165
  case (dest_type || type)
165
166
  when :boolean then value ? 'true' : 'false'
166
- when :date, :time, :datetime, :timestamp then value.strftime(Format::SOLR_TIME_FORMAT)
167
+ when :date, :time, :datetime, :timestamp then value.to_time.utc.strftime(Format::SOLR_TIME_FORMAT)
167
168
  when :list, :set then self.list_to_solr_value(value)
168
169
  when :map then self.map_to_solr_value(value)
169
170
  else value
@@ -171,19 +172,19 @@ module DatastaxRails
171
172
  end
172
173
 
173
174
  def list_to_solr_value(value)
174
- value.map {|v| type_cast_for_solr(v, @options[:holds])}
175
+ value.map {|v| type_cast_for_solr(v, @options[:holds].to_sym)}
175
176
  end
176
177
 
177
178
  def map_to_solr_value(value)
178
- value.each {|k,v| value[k] = type_cast_for_solr(v, @options[:holds])}
179
+ value.each {|k,v| value[k] = type_cast_for_solr(v, @options[:holds].to_sym)}
179
180
  end
180
181
 
181
182
  def list_to_cql3_value(value)
182
- value.map {|v| type_cast_for_cql3(v, @options[:holds])}
183
+ value.map {|v| type_cast_for_cql3(v, @options[:holds].to_sym)}
183
184
  end
184
185
 
185
186
  def map_to_cql3_value(value)
186
- value.dup.each {|k,v| value[k] = type_cast_for_cql3(v, @options[:holds])}
187
+ value.dup.each {|k,v| value[k] = type_cast_for_cql3(v, @options[:holds].to_sym)}
187
188
  value
188
189
  end
189
190
 
@@ -210,7 +211,7 @@ module DatastaxRails
210
211
  end
211
212
 
212
213
  def full_solr_range
213
- if solr_type == 'date'
214
+ if %w[date uuid].include? solr_type
214
215
  '[* TO *]'
215
216
  else
216
217
  '[\"\" TO *]'
@@ -33,17 +33,17 @@ module DatastaxRails
33
33
  # ts_ prefix to differentiate it from texts.
34
34
  #
35
35
  # class Item < DatastaxRails::DynamicModel
36
- # self.group_by = 'item'
36
+ # self.grouping = 'item'
37
37
  # timestamps
38
38
  # end
39
39
  #
40
40
  # class CoreMetadata < DatastaxRails::DynamicModel
41
- # self.group_by = 'core'
41
+ # self.grouping = 'core'
42
42
  # timestamps
43
43
  # end
44
44
  #
45
45
  # class TeamMetadata < DatastaxRails::DynamicModel
46
- # self.group_by = 'team'
46
+ # self.grouping = 'team'
47
47
  # timestamps
48
48
  # end
49
49
  #
@@ -60,39 +60,79 @@ module DatastaxRails
60
60
  # the collection so:
61
61
  #
62
62
  # Item.first.strings #=> {s_title: "Title"}
63
+ #
64
+ # If you would like to still define known attributes ahead of time, you can still do so:
65
+ #
66
+ # class TeamMetadata < DatastaxRails::DynamicModel
67
+ # self.grouping = 'team'
68
+ # string :name
69
+ # timestamps
70
+ # end
71
+ #
72
+ # TeamMetadata.new(name: 'John').name #=> 'John'
73
+ # TeamMetadata.new(name: 'John').strings #=> {'s_name' => 'John'}
74
+ #
75
+ # Getters and setters are automatically created to map to the attribute stored in the hash.
76
+ # In addition, there is a helper method to map column names to the field name in solr to
77
+ # assist with search.
63
78
  class DynamicModel < WideStorageModel
64
79
  self.abstract_class = true
65
80
 
66
- class_attribute :group_by_attribute
67
-
68
- def self.group_by=(group)
69
- self.group_by_attribute = group
70
- self.attribute_definitions['group'].default = group
71
- default_scope -> {where('group' => group)}
72
- end
81
+ PREFIXES = {string: :s_, text: :t_, boolean: :b_, date: :d_,
82
+ timestamp: :ts_, integer: :i_, float: :f_, uuid: :u_}.with_indifferent_access
73
83
 
84
+ class_attribute :group_by_attribute
85
+ class_attribute :declared_attributes
74
86
 
75
- def self.inherited(child)
76
- super
77
- child.column_family = 'dynamic_model'
78
- child.primary_key = 'id'
79
- child.cluster_by = 'group'
80
- child.uuid :id
81
- child.string :group
82
- child.map :s_, :holds => :string
83
- child.map :t_, :holds => :text
84
- child.map :b_, :holds => :boolean
85
- child.map :d_, :holds => :date
86
- child.map :ts_, :holds => :timestamp
87
- child.map :i_, :holds => :integer
88
- child.map :f_, :holds => :float
89
- child.map :u_, :holds => :uuid
87
+ class << self
88
+ def grouping=(group)
89
+ self.group_by_attribute = group
90
+ self.attribute_definitions['group'].default = group
91
+ default_scope -> {where('group' => group)}
92
+ end
93
+
94
+ alias_method :_attribute, :attribute
95
+
96
+ def attribute(name, options)
97
+ options.symbolize_keys!
98
+ return super if [:map,:list,:set].include?(options[:type].to_sym)
99
+ # Only type supported for now
100
+ options.assert_valid_keys(:type)
101
+ raise ArgumentError, "Invalid type specified for dynamic attribute: '#{name}: #{options[:type]}'" unless PREFIXES.has_key?(options[:type])
102
+ self.declared_attributes[name] = PREFIXES[options[:type]].to_s + name.to_s
103
+ define_method(name) do
104
+ self.send(PREFIXES[options[:type]])[name]
105
+ end
106
+ define_method("#{name.to_s}=") do |val|
107
+ self.send(PREFIXES[options[:type]])[name] = val
108
+ end
109
+ end
90
110
 
91
- child.map_columns.each do |col|
92
- child.instance_eval do
93
- alias_attribute col.options[:holds].to_s.pluralize, col.name
111
+ def inherited(child)
112
+ super
113
+ child.declared_attributes = child.declared_attributes.nil? ? {}.with_indifferent_access : child.declared_attributes.dup
114
+ child.column_family = 'dynamic_model'
115
+ child.primary_key = 'id'
116
+ child.cluster_by = 'group'
117
+ child._attribute :id, :type => :uuid
118
+ child._attribute :group, :type => :string
119
+ PREFIXES.each do |k,v|
120
+ child._attribute v, holds: k.to_sym, type: :map
121
+ child.instance_eval { alias_attribute k.to_s.pluralize, v}
94
122
  end
95
123
  end
124
+
125
+ def solr_field_name(attr, type = nil)
126
+ if type
127
+ PREFIXES[type].to_s + attr.to_s
128
+ else
129
+ declared_attributes[attr] || raise(UnknownAttributeError, "Unknown attribute: #{attr}. You must specify a type.")
130
+ end
131
+ end
132
+ end
133
+
134
+ def solr_field_name(attr, type = nil)
135
+ self.class.solr_field_name(attr, type)
96
136
  end
97
137
  end
98
138
  end
@@ -399,7 +399,7 @@ module DatastaxRails
399
399
  end
400
400
  attributes.delete(k)
401
401
  else
402
- attributes[k] = solr_format(v)
402
+ attributes[k] = solr_format(k,v)
403
403
  end
404
404
  end
405
405
  r.where_values << attributes unless attributes.empty?
@@ -443,7 +443,7 @@ module DatastaxRails
443
443
  end
444
444
  attributes.delete(k)
445
445
  else
446
- attributes[k] = solr_format(v)
446
+ attributes[k] = solr_format(k,v)
447
447
  end
448
448
  end
449
449
  r.where_not_values << attributes unless attributes.empty?
@@ -536,27 +536,26 @@ module DatastaxRails
536
536
  end
537
537
 
538
538
  # Formats a value for solr (assuming this is a solr query).
539
- def solr_format(value)
539
+ def solr_format(attribute, value)
540
540
  return value unless use_solr_value
541
+ column = attribute.is_a?(DatastaxRails::Column) ? attribute : klass.column_for_attribute(attribute)
542
+ # value = column.type_cast_for_solr(value)
541
543
  case
542
- when value.is_a?(Time)
543
- value.utc.strftime(DatastaxRails::Column::Format::SOLR_TIME_FORMAT)
544
- when value.is_a?(DateTime)
545
- value.to_time.utc.strftime(DatastaxRails::Column::Format::SOLR_TIME_FORMAT)
546
- when value.is_a?(Date)
547
- value.strftime(DatastaxRails::Column::Format::SOLR_TIME_FORMAT)
548
- when value.is_a?(Array)
549
- value.collect {|v| v.to_s.gsub(/ /,"\\ ") }.join(" OR ")
544
+ when value.is_a?(Time) || value.is_a?(DateTime) || value.is_a?(Date)
545
+ column.type_cast_for_solr(value)
546
+ when value.is_a?(Array) || value.is_a?(Set)
547
+ column.type_cast_for_solr(value).collect {|v| v.to_s.gsub(/ /,"\\ ") }.join(" OR ")
550
548
  when value.is_a?(Fixnum)
551
549
  value < 0 ? "\\#{value}" : value
552
550
  when value.is_a?(Range)
553
- "[#{solr_format(value.first)} TO #{solr_format(value.last)}]"
551
+ "[#{solr_format(attribute, value.first)} TO #{solr_format(attribute, value.last)}]"
554
552
  when value.is_a?(String)
555
553
  solr_escape(downcase_query(value.gsub(/ /,"\\ ")))
556
554
  when value.is_a?(FalseClass), value.is_a?(TrueClass)
557
555
  value.to_s
558
556
  else
559
557
  value
558
+
560
559
  end
561
560
  end
562
561
 
@@ -571,9 +570,9 @@ module DatastaxRails
571
570
  def equal_to(value) #:nodoc:
572
571
  @relation.clone.tap do |r|
573
572
  if @invert
574
- r.where_not_values << {@attribute => r.solr_format(value)}
573
+ r.where_not_values << {@attribute => r.solr_format(@attribute, value)}
575
574
  else
576
- r.where_values << {@attribute => r.solr_format(value)}
575
+ r.where_values << {@attribute => r.solr_format(@attribute, value)}
577
576
  end
578
577
  end
579
578
  end
@@ -581,9 +580,9 @@ module DatastaxRails
581
580
  def greater_than(value) #:nodoc:
582
581
  @relation.clone.tap do |r|
583
582
  if @invert
584
- r.less_than_values << {@attribute => r.solr_format(value)}
583
+ r.less_than_values << {@attribute => r.solr_format(@attribute, value)}
585
584
  else
586
- r.greater_than_values << {@attribute => r.solr_format(value)}
585
+ r.greater_than_values << {@attribute => r.solr_format(@attribute, value)}
587
586
  end
588
587
  end
589
588
  end
@@ -591,9 +590,9 @@ module DatastaxRails
591
590
  def less_than(value) #:nodoc:
592
591
  @relation.clone.tap do |r|
593
592
  if @invert
594
- r.greater_than_values << {@attribute => r.solr_format(value)}
593
+ r.greater_than_values << {@attribute => r.solr_format(@attribute, value)}
595
594
  else
596
- r.less_than_values << {@attribute => r.solr_format(value)}
595
+ r.less_than_values << {@attribute => r.solr_format(@attribute, value)}
597
596
  end
598
597
  end
599
598
  end
@@ -16,10 +16,11 @@ module DatastaxRails
16
16
 
17
17
  def migrate_all(force = false)
18
18
  say_with_time("Migrating all models") do
19
- # Ensure all models are loaded (necessary for non-production mode)
20
- Dir[Rails.root.join("app","models",'*.rb').to_s].each do |file|
21
- require File.basename(file, File.extname(file))
19
+
20
+ FileList[rails_models].each do |model|
21
+ require model
22
22
  end
23
+
23
24
  count = 0
24
25
  DatastaxRails::Base.models.each do |m|
25
26
  if !m.abstract_class?
@@ -52,6 +53,18 @@ module DatastaxRails
52
53
  end
53
54
 
54
55
  private
56
+
57
+ # Determine all models to be included within the migration
58
+ # using Rails config paths instead of absolute paths.
59
+ # This enables Rails Engines to monkey patch their own
60
+ # models in, to be automatically included within migrations.
61
+ #
62
+ # @see http://pivotallabs.com/leave-your-migrations-in-your-rails-engines/
63
+ #
64
+ # @return [Array] list of configured application models
65
+ def rails_models
66
+ Rails.configuration.paths['app/models'].expanded.map { |p| p + '/*.rb' }
67
+ end
55
68
 
56
69
  # Checks to ensure that the schema_migrations column family exists and creates it if not
57
70
  def check_schema_migrations
@@ -19,6 +19,11 @@ module DatastaxRails
19
19
 
20
20
  hash
21
21
  end
22
+
23
+ def as_json(options = {})
24
+ json = super(options)
25
+ json.each {|k,v| json[k] = v.to_s if v.is_a?(::Cql::Uuid)}
26
+ end
22
27
 
23
28
  private
24
29
  # Add associations specified via the <tt>:include</tt> option.
@@ -20,13 +20,12 @@ module DatastaxRails
20
20
  super(convert_key(key))
21
21
  end
22
22
 
23
- protected
24
- def convert_key(key)
25
- unless key.to_s.starts_with?(name)
26
- key = name + key.to_s
27
- end
28
- super(key)
23
+ def convert_key(key)
24
+ unless key.to_s.starts_with?(name)
25
+ key = name + key.to_s
29
26
  end
27
+ super(key)
28
+ end
30
29
  end
31
30
  end
32
31
  end
@@ -1,4 +1,3 @@
1
1
  module DatastaxRails
2
- # The current version of the gem
3
- VERSION = "2.0.3"
2
+ VERSION = "2.0.4"
4
3
  end
@@ -8,12 +8,18 @@ describe DatastaxRails::Base do
8
8
  end
9
9
 
10
10
  it "should run after_save" do
11
- Person.commit_solr
12
- p = Person.new(:name => "Jason")
13
- p.save!
11
+ p = Person.new(:name => "Steve")
12
+ p.save
14
13
  p.instance_variable_get(:@after_save_ran).should == "yup"
15
14
  end
16
15
 
16
+ it "should run after_create" do
17
+ p = Person.new(:name => "Tommy")
18
+ p.save
19
+ p.instance_variable_get(:@after_create_ran).should == "yup"
20
+ end
21
+
22
+
17
23
  it "should raise RecordNotFound when finding a bogus ID" do
18
24
  lambda { Person.find("xyzzy") }.should raise_exception(DatastaxRails::RecordNotFound)
19
25
  end
@@ -26,20 +32,22 @@ describe DatastaxRails::Base do
26
32
  end
27
33
 
28
34
  it "considers a new object to be unequal to a saved object" do
29
- p1=Person.create!(:name => 'John')
30
- p2=Person.new(:name => 'John')
35
+ p1=Person.create!(:name => 'Mike')
36
+ p2=Person.new(:name => 'Mike')
31
37
  expect(p1).not_to eq(p2)
32
38
  end
33
39
 
34
40
  it "considers two persisted objects to be equal if their primary keys are equal" do
35
- p1=Person.create!(:name => 'John')
41
+ Person.commit_solr
42
+ p1=Person.create!(:name => 'Jim')
36
43
  p2=Person.find(p1.id)
37
44
  expect(p1).to eq(p2)
38
45
  end
39
46
 
40
47
  it "considers two persisted objects to be unequal if they have different primary keys" do
41
- p1=Person.create!(:name => 'John')
42
- p2=Person.create!(:name => 'James')
48
+ Person.commit_solr
49
+ p1=Person.create!(:name => 'Kirk')
50
+ p2=Person.create!(:name => 'Tiberious')
43
51
  expect(p1).not_to eq(p2)
44
52
  end
45
53
  end
@@ -197,6 +197,7 @@ describe DatastaxRails::Column do
197
197
 
198
198
  describe "map" do
199
199
  let(:c) {DatastaxRails::Column.new("field_", nil, "map", :holds => :integer)}
200
+ let(:dc) {DatastaxRails::Column.new("field_", nil, "map", :holds => :date)}
200
201
 
201
202
  it "casts map keys to strings" do
202
203
  expect(c.type_cast({:field_key => 7}, record)).to eq({"field_key" => 7})
@@ -209,6 +210,14 @@ describe DatastaxRails::Column do
209
210
  it "wraps map values in a DynamicMap" do
210
211
  expect(c.type_cast({'field_key' => '7'}, record)).to be_a(DatastaxRails::Types::DynamicMap)
211
212
  end
213
+
214
+ describe "to cql" do
215
+ it "casts map values to the appropriate type" do
216
+ date = Date.parse("1980-10-19")
217
+ time = Time.parse("1980-10-19 00:00:00 +0000")
218
+ expect(dc.type_cast_for_cql3({:field_key => date})).to eq(:field_key => time)
219
+ end
220
+ end
212
221
  end
213
222
 
214
223
  describe "list" do
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ class DynamicTestModel1 < DatastaxRails::DynamicModel
4
+ self.grouping = 'test1'
5
+ string :name
6
+ timestamps
7
+ end
8
+
9
+ class DynamicTestModel2 < DatastaxRails::DynamicModel
10
+ self.grouping = 'test2'
11
+ end
12
+
13
+ describe DatastaxRails::DynamicModel do
14
+ let(:one) {DynamicTestModel1.new}
15
+ let(:two) {DynamicTestModel2.new}
16
+
17
+ it {expect(one).to respond_to(:created_at)}
18
+ it {expect(one).to respond_to(:created_at=)}
19
+ it {expect(two).not_to respond_to(:created_at)}
20
+ it {expect(two).not_to respond_to(:created_at=)}
21
+
22
+ it "sets the attribute in the dynamic collection" do
23
+ one.name = 'John'
24
+ expect(one.s_).to eq('s_name' => 'John')
25
+ end
26
+
27
+ it "retrieves the attribute from the dynamic collection" do
28
+ one.strings[:name] = 'John'
29
+ expect(one.name).to eq('John')
30
+ end
31
+
32
+ describe "#solr_field_name" do
33
+ it "maps a attribute name to the underlying storage key" do
34
+ expect(one.solr_field_name(:name)).to eq('s_name')
35
+ end
36
+
37
+ it "raises DatastaxRails::UnknownAttributeError if an unknown attribute is mapped without a type" do
38
+ expect{two.solr_field_name(:name)}.to raise_exception(DatastaxRails::UnknownAttributeError)
39
+ end
40
+
41
+ it "maps an undeclared attribute if a type is given" do
42
+ expect(one.solr_field_name(:birthdate, :date)).to eq('d_birthdate')
43
+ end
44
+ end
45
+ end
@@ -228,20 +228,23 @@ describe DatastaxRails::Relation do
228
228
  describe '#solr_format' do
229
229
  context 'when formatting Time' do
230
230
  let(:time) { Time.new 2011, 10, 9, 8, 7, 6, "-05:00" }
231
+ let(:c) {DatastaxRails::Column.new("field", nil, "time")}
231
232
 
232
- it { expect(@relation.solr_format(time)).to eq '2011-10-09T13:07:06Z' }
233
+ it { expect(@relation.solr_format(c,time)).to eq '2011-10-09T13:07:06Z' }
233
234
  end
234
235
 
235
236
  context 'when formatting Date' do
236
237
  let(:date) { Date.new 2001, 2, 3 }
238
+ let(:c) {DatastaxRails::Column.new("field", nil, "date")}
237
239
 
238
- it { expect(@relation.solr_format(date)).to eq '2001-02-03T00:00:00Z' }
240
+ it { expect(@relation.solr_format(c,date)).to eq '2001-02-03T00:00:00Z' }
239
241
  end
240
242
 
241
243
  context 'when formatting DateTime' do
242
244
  let(:datetime) { DateTime.new 2001, 2, 3, 4, 5, 6, "-07:00" }
245
+ let(:c) {DatastaxRails::Column.new("field", nil, "timestamp")}
243
246
 
244
- it { expect(@relation.solr_format(datetime)).to eq '2001-02-03T11:05:06Z' }
247
+ it { expect(@relation.solr_format(c,datetime)).to eq '2001-02-03T11:05:06Z' }
245
248
  end
246
249
  end
247
250
  end
data/spec/spec_helper.rb CHANGED
@@ -21,7 +21,7 @@ RSpec.configure do |config|
21
21
  # Filter slow specs. Add a :slow tag to the spec to keep it from
22
22
  # running unless the SLOW_SPECS environment variable is set.
23
23
  config.treat_symbols_as_metadata_keys_with_true_values = true
24
- config.filter_run_excluding :slow unless ENV['SLOW_SPECS']
24
+ # config.filter_run_excluding :slow unless ENV['SLOW_SPECS']
25
25
 
26
26
  config.before(:each) do
27
27
  DatastaxRails::Base.recorded_classes = {}
@@ -16,6 +16,7 @@ class Person < DatastaxRails::Base
16
16
  before_create :set_variable2
17
17
  before_save :set_nickname
18
18
  after_save :set_variable
19
+ after_create :set_variable3
19
20
 
20
21
  validates :name, :presence => true, :uniqueness => :true
21
22
 
@@ -30,6 +31,10 @@ class Person < DatastaxRails::Base
30
31
  def set_variable2
31
32
  @before_create_ran = "yup"
32
33
  end
34
+
35
+ def set_variable3
36
+ @after_create_ran = 'yup'
37
+ end
33
38
  end
34
39
 
35
40
  class Car < DatastaxRails::Base
@@ -103,9 +108,9 @@ class Hobby < DatastaxRails::Base
103
108
  end
104
109
 
105
110
  class CoreMetadata < DatastaxRails::DynamicModel
106
- self.group_by = 'core'
111
+ self.grouping = 'core'
107
112
  end
108
113
 
109
114
  class TeamMetadata < DatastaxRails::DynamicModel
110
- self.group_by = 'team'
115
+ self.grouping = 'team'
111
116
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: datastax_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.3
4
+ version: 2.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason M. Kusar
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-22 00:00:00.000000000 Z
11
+ date: 2014-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -246,6 +246,7 @@ files:
246
246
  - spec/datastax_rails/column_spec.rb
247
247
  - spec/datastax_rails/cql/select_spec.rb
248
248
  - spec/datastax_rails/cql/update_spec.rb
249
+ - spec/datastax_rails/dynamic_model_spec.rb
249
250
  - spec/datastax_rails/inheritance_spec.rb
250
251
  - spec/datastax_rails/persistence_spec.rb
251
252
  - spec/datastax_rails/relation/batches_spec.rb
@@ -336,6 +337,7 @@ test_files:
336
337
  - spec/support/models.rb
337
338
  - spec/support/default_consistency_shared_examples.rb
338
339
  - spec/support/datastax_test_hook.rb
340
+ - spec/datastax_rails/dynamic_model_spec.rb
339
341
  - spec/datastax_rails/attribute_methods_spec.rb
340
342
  - spec/datastax_rails/scoping/default_spec.rb
341
343
  - spec/datastax_rails/associations_spec.rb