datastax_rails 2.0.3 → 2.0.4

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