datastax_rails 2.0.16 → 2.0.18

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: 132b266af9a95c9c39cf2ebd31070736456203a9
4
- data.tar.gz: 456554a5b9f107ceb7766f8dc8d80b9e842ef53a
3
+ metadata.gz: 19e0c7c773fb3991c35ee3c5a4f76ec967d9041c
4
+ data.tar.gz: 4a5033007b3009ea785e38935f0d0a7c5a6eb410
5
5
  SHA512:
6
- metadata.gz: f91a8dcaba63be7244c3d58621fab35a9677e7cf1c93e9763f6adf9ef25b203a6883531100c7a357082b60e980435c1f7a276eab0f87ed35758be4701a3debe5
7
- data.tar.gz: 773c2de70abae6deae8ba0119873c760b61cb14e0992c88e39975446136a82856f1fc177802b23a78e378ece41b075942adef07d5b13f2af891d49967c31f3c1
6
+ metadata.gz: f3ead232f919376053a15e813cec9d08630ff7ae4414300590ff2b8500077b9c6622a5347dec3d2ce5f25e22a528df05399843d8eff6587ef24df14ba1cc7be1
7
+ data.tar.gz: 4f620b9de99f047c114386357424cd1516432f81a45c1c3d446d31343712cd67c4396f46d2dab624b75361026dd58905982553540e8872c8bed9d06a3b98eac5
data/README.rdoc CHANGED
@@ -15,10 +15,11 @@ Significant changes from SolandraObject:
15
15
 
16
16
  === Usage Note
17
17
 
18
- Before using this gem, you should probably take a strong look at the type of problem you are trying to solve. Cassandra is primarily
19
- designed as a solution to Big Data problems. This gem is not. You will notice that it still carries a lot of relational logic with it.
20
- We are using DSE to solve a replication problem more so than a Big Data problem. That's not to say that this gem won't work for Big Data problems,
21
- it just might not be ideal. You've been warned...
18
+ Before using this gem, you should probably take a strong look at the type of problem you are trying to solve.
19
+ Cassandra is primarily designed as a solution to Big Data problems. This gem is not. You will notice that it
20
+ still carries a lot of relational logic with it. We are using DSE to solve a replication problem more so than
21
+ a Big Data problem. That's not to say that this gem won't work for Big Data problems, it just might not be
22
+ ideal. You've been warned...
22
23
 
23
24
  === Getting started
24
25
 
@@ -31,6 +32,7 @@ Configure the config/datastax.yml file:
31
32
  development:
32
33
  servers: ["127.0.0.1"]
33
34
  port: 9042
35
+ ssl: true
34
36
  keyspace: "<my_app>_development"
35
37
  strategy_class: "org.apache.cassandra.locator.NetworkTopologyStrategy"
36
38
  strategy_options: {"DC1": "1"}
@@ -48,6 +50,7 @@ For a more simple, single datacenter setup, something like this should probably
48
50
  development:
49
51
  servers: ["127.0.0.1"]
50
52
  port: 9042
53
+ ssl: false
51
54
  keyspace: "datastax_rails_development"
52
55
  strategy_class: "org.apache.cassandra.locator.SimpleStrategy"
53
56
  strategy_options: {"replication_factor": "1"}
@@ -70,20 +70,14 @@
70
70
  the classpath, this is useful for including all jars in a
71
71
  directory.
72
72
  -->
73
- <lib dir="../../contrib/extraction/lib" />
74
73
  <!-- When a regex is specified in addition to a directory, only the
75
74
  files in that directory which completely match the regex
76
75
  (anchored on both ends) will be included.
77
76
  -->
78
- <lib dir="../../dist/" regex="apache-solr-cell-\d.*\.jar" />
79
- <lib dir="../../dist/" regex="apache-solr-clustering-\d.*\.jar" />
80
- <lib dir="../../dist/" regex="apache-solr-dataimporthandler-\d.*\.jar" />
81
77
 
82
78
  <!-- If a dir option (with or without a regex) is used and nothing
83
79
  is found that matches, it will be ignored
84
80
  -->
85
- <lib dir="../../contrib/clustering/lib/" />
86
- <lib dir="/total/crap/dir/ignored" />
87
81
  <!-- an exact path can be used to specify a specific file. This
88
82
  will cause a serious error to be logged if it can't be loaded.
89
83
  -->
@@ -219,8 +213,7 @@
219
213
  triggering a new commit.
220
214
  -->
221
215
  <autoSoftCommit>
222
- <maxTime>1000</maxTime>
223
- <maxDocs>1</maxDocs>
216
+ <maxTime><%= @solr_commit_time %></maxTime>
224
217
  </autoSoftCommit>
225
218
 
226
219
  <!-- Update Related Event Listeners
@@ -12,7 +12,7 @@ Cql::Client::ConnectionManager.class_eval do
12
12
  attr_reader :current_connection
13
13
 
14
14
  def random_connection
15
- fail NotConnectedError unless connected?
15
+ fail ::Cql::NotConnectedError unless connected?
16
16
  @lock.synchronize do
17
17
  @count ||= 0
18
18
  @count += 1
@@ -9,6 +9,7 @@ module DatastaxRails
9
9
  autoload :Associations
10
10
  autoload :AttributeAssignment
11
11
  autoload :AttributeMethods
12
+ autoload :AutosaveAssociation
12
13
  autoload :Base
13
14
  autoload :Batches
14
15
  autoload :Callbacks
@@ -101,3 +102,7 @@ require 'datastax_rails/errors'
101
102
  require 'cql-rb_extensions'
102
103
 
103
104
  ActiveSupport.run_load_hooks(:datastax_rails, DatastaxRails::Base)
105
+
106
+ ActiveSupport.on_load(:i18n) do
107
+ I18n.load_path << File.dirname(__FILE__) + '/datastax_rails/locale/en.yml'
108
+ end
@@ -62,7 +62,7 @@ module DatastaxRails
62
62
  @association_cache[name] = association
63
63
  end
64
64
 
65
- module ClassMethods
65
+ module ClassMethods # rubocop:disable Style/Documentation
66
66
  def belongs_to(name, options = {})
67
67
  Builder::BelongsTo.build(self, name, options)
68
68
  end
@@ -1,6 +1,7 @@
1
1
  module DatastaxRails
2
2
  module Associations
3
- class AssociationScope #:nodoc:
3
+ # Creates the scope (relation) for the assocation
4
+ class AssociationScope
4
5
  attr_reader :association
5
6
 
6
7
  delegate :klass, :owner, :reflection, to: :association
@@ -1,6 +1,17 @@
1
1
  module DatastaxRails
2
2
  module Associations
3
- class BelongsToAssociation < SingularAssociation #:nodoc:
3
+ # belongs_to associations are the child side of a parent/child relationship
4
+ #
5
+ # class Car < DatastaxRails::Base
6
+ # uuid :id
7
+ # uuid :owner_id
8
+ # belongs_to :owner, class_name: 'Person'
9
+ # end
10
+ #
11
+ # Valid options:
12
+ # * class_name - The class on the other side (if different than what +classify+ returns)
13
+ # * foreign_key - The name of the foreign_key column if not just _id at the end of the association name
14
+ class BelongsToAssociation < SingularAssociation
4
15
  attr_reader :updated
5
16
  alias_method :updated?, :updated
6
17
  def replace(record)
@@ -16,11 +16,14 @@ module DatastaxRails::Associations::Builder # rubocop:disable Style/ClassAndModu
16
16
  @model, @name, @options = model, name, options
17
17
  end
18
18
 
19
+ include Module.new { def build; end }
20
+
19
21
  def build
20
22
  validate_options
21
- reflection = model.create_reflection(self.class.macro, name, options, model)
23
+ @reflection = model.create_reflection(self.class.macro, name, options, model)
22
24
  define_accessors
23
- reflection
25
+ super # provides an extension point
26
+ @reflection
24
27
  end
25
28
 
26
29
  def mixin
@@ -1,6 +1,7 @@
1
1
  module DatastaxRails
2
2
  module Associations
3
- class HasOneAssociation < SingularAssociation #:nodoc:
3
+ # A has_one association is the parent of a one-to-one parent/child relation
4
+ class HasOneAssociation < SingularAssociation
4
5
  def replace(record, save = true)
5
6
  raise_on_type_mismatch(record) if record
6
7
  load_target
@@ -1,6 +1,7 @@
1
1
  module DatastaxRails
2
2
  module Associations
3
- class SingularAssociation < Association #:nodoc:
3
+ # Encapsulates the common functionality between belongs_to and has_one relationships
4
+ class SingularAssociation < Association
4
5
  # Implements the reader method, e.g. foo.bar for Foo.has_one :bar
5
6
  def reader(force_reload = false)
6
7
  reload if force_reload || !loaded? || stale_target?
@@ -0,0 +1,429 @@
1
+ # rubocop:disable
2
+ module DatastaxRails
3
+ # = DatastaxRails Autosave Association
4
+ #
5
+ # +AutosaveAssociation+ is a module that takes care of automatically saving
6
+ # associated records when their parent is saved. In addition to saving, it
7
+ # also destroys any associated records that were marked for destruction.
8
+ # (See +mark_for_destruction+ and <tt>marked_for_destruction?</tt>).
9
+ #
10
+ # If validations for any of the associations fail, their error messages will
11
+ # be applied to the parent.
12
+ #
13
+ # Note that it also means that associations marked for destruction won't
14
+ # be destroyed directly. They will however still be marked for destruction.
15
+ #
16
+ # Note that <tt>autosave: false</tt> is not same as not declaring <tt>:autosave</tt>.
17
+ # When the <tt>:autosave</tt> option is not present then new association records are
18
+ # saved but the updated association records are not saved.
19
+ #
20
+ # == Validation
21
+ #
22
+ # Children records are validated unless <tt>:validate</tt> is +false+.
23
+ #
24
+ # == Callbacks
25
+ #
26
+ # Association with autosave option defines several callbacks on your
27
+ # model (before_save, after_create, after_update). Please note that
28
+ # callbacks are executed in the order they were defined in the
29
+ # model. You should avoid modifying the association content, before
30
+ # autosave callbacks are executed. Placing your callbacks after
31
+ # associations is usually a good practice.
32
+ #
33
+ # === One-to-one Example
34
+ #
35
+ # class Post
36
+ # has_one :author, autosave: true
37
+ # end
38
+ #
39
+ # Saving changes to the parent and its associated model can now be performed
40
+ # automatically:
41
+ #
42
+ # post = Post.find(1)
43
+ # post.title # => "The current global position of migrating ducks"
44
+ # post.author.name # => "alloy"
45
+ #
46
+ # post.title = "On the migration of ducks"
47
+ # post.author.name = "Eloy Duran"
48
+ #
49
+ # post.save
50
+ # post.reload
51
+ # post.title # => "On the migration of ducks"
52
+ # post.author.name # => "Eloy Duran"
53
+ #
54
+ # Destroying an associated model, as part of the parent's save action, is as
55
+ # simple as marking it for destruction:
56
+ #
57
+ # post.author.mark_for_destruction
58
+ # post.author.marked_for_destruction? # => true
59
+ #
60
+ # Note that the model is _not_ yet removed from the database:
61
+ #
62
+ # id = post.author.id
63
+ # Author.find_by(id: id).nil? # => false
64
+ #
65
+ # post.save
66
+ # post.reload.author # => nil
67
+ #
68
+ # Now it _is_ removed from the database:
69
+ #
70
+ # Author.find_by(id: id).nil? # => true
71
+ #
72
+ # === One-to-many Example
73
+ #
74
+ # When <tt>:autosave</tt> is not declared new children are saved when their parent is saved:
75
+ #
76
+ # class Post
77
+ # has_many :comments # :autosave option is not declared
78
+ # end
79
+ #
80
+ # post = Post.new(title: 'ruby rocks')
81
+ # post.comments.build(body: 'hello world')
82
+ # post.save # => saves both post and comment
83
+ #
84
+ # post = Post.create(title: 'ruby rocks')
85
+ # post.comments.build(body: 'hello world')
86
+ # post.save # => saves both post and comment
87
+ #
88
+ # post = Post.create(title: 'ruby rocks')
89
+ # post.comments.create(body: 'hello world')
90
+ # post.save # => saves both post and comment
91
+ #
92
+ # When <tt>:autosave</tt> is true all children are saved, no matter whether they
93
+ # are new records or not:
94
+ #
95
+ # class Post
96
+ # has_many :comments, autosave: true
97
+ # end
98
+ #
99
+ # post = Post.create(title: 'ruby rocks')
100
+ # post.comments.create(body: 'hello world')
101
+ # post.comments[0].body = 'hi everyone'
102
+ # post.save # => saves both post and comment, with 'hi everyone' as body
103
+ #
104
+ # Destroying one of the associated models as part of the parent's save action
105
+ # is as simple as marking it for destruction:
106
+ #
107
+ # post.comments.last.mark_for_destruction
108
+ # post.comments.last.marked_for_destruction? # => true
109
+ # post.comments.length # => 2
110
+ #
111
+ # Note that the model is _not_ yet removed from the database:
112
+ #
113
+ # id = post.comments.last.id
114
+ # Comment.find_by(id: id).nil? # => false
115
+ #
116
+ # post.save
117
+ # post.reload.comments.length # => 1
118
+ #
119
+ # Now it _is_ removed from the database:
120
+ #
121
+ # Comment.find_by(id: id).nil? # => true
122
+ module AutosaveAssociation
123
+ extend ActiveSupport::Concern
124
+
125
+ # Extends the association builder to add autosave callbacks
126
+ module AssociationBuilderExtension
127
+ def build
128
+ model.send(:add_autosave_association_callbacks, reflection)
129
+ super
130
+ end
131
+ end
132
+
133
+ included do
134
+ require 'datastax_rails/associations/builder/association'
135
+ Associations::Builder::Association.class_eval do
136
+ valid_options << :autosave
137
+ include AssociationBuilderExtension
138
+ end
139
+ end
140
+
141
+ module ClassMethods # rubocop:disable Style/Documentation
142
+ private
143
+
144
+ def define_non_cyclic_method(name, reflection, &block)
145
+ define_method(name) do |*_args|
146
+ result = true
147
+ @_already_called ||= {}
148
+ # Loop prevention for validation of associations
149
+ unless @_already_called[[name, reflection.name]]
150
+ begin
151
+ @_already_called[[name, reflection.name]] = true
152
+ result = instance_eval(&block)
153
+ ensure
154
+ @_already_called[[name, reflection.name]] = false
155
+ end
156
+ end
157
+
158
+ result
159
+ end
160
+ end
161
+
162
+ # Adds validation and save callbacks for the association as specified by
163
+ # the +reflection+.
164
+ #
165
+ # For performance reasons, we don't check whether to validate at runtime.
166
+ # However the validation and callback methods are lazy and those methods
167
+ # get created when they are invoked for the very first time. However,
168
+ # this can change, for instance, when using nested attributes, which is
169
+ # called _after_ the association has been defined. Since we don't want
170
+ # the callbacks to get defined multiple times, there are guards that
171
+ # check if the save or validation methods have already been defined
172
+ # before actually defining them.
173
+ def add_autosave_association_callbacks(reflection)
174
+ save_method = :"autosave_associated_records_for_#{reflection.name}"
175
+ validation_method = :"validate_associated_records_for_#{reflection.name}"
176
+ collection = reflection.collection?
177
+
178
+ unless method_defined?(save_method)
179
+ if collection
180
+ before_save :before_save_collection_association
181
+
182
+ define_non_cyclic_method(save_method, reflection) { save_collection_association(reflection) }
183
+ # Doesn't use after_save as that would save associations added in after_create/after_update twice
184
+ after_create save_method
185
+ after_update save_method
186
+ elsif reflection.macro == :has_one
187
+ define_method(save_method) { save_has_one_association(reflection) }
188
+ # Configures two callbacks instead of a single after_save so that
189
+ # the model may rely on their execution order relative to its
190
+ # own callbacks.
191
+ #
192
+ # For example, given that after_creates run before after_saves, if
193
+ # we configured instead an after_save there would be no way to fire
194
+ # a custom after_create callback after the child association gets
195
+ # created.
196
+ after_create save_method
197
+ after_update save_method
198
+ else
199
+ define_non_cyclic_method(save_method, reflection) { save_belongs_to_association(reflection) }
200
+ before_save save_method
201
+ end
202
+ end
203
+
204
+ if reflection.validate? && !method_defined?(validation_method) # rubocop:disable Style/GuardClause
205
+ method = (collection ? :validate_collection_association : :validate_single_association)
206
+ define_non_cyclic_method(validation_method, reflection) { send(method, reflection) }
207
+ validate validation_method
208
+ end
209
+ end
210
+ end
211
+
212
+ # Reloads the attributes of the object as usual and clears <tt>marked_for_destruction</tt> flag.
213
+ def reload(options = nil)
214
+ @marked_for_destruction = false
215
+ @destroyed_by_association = nil
216
+ super
217
+ end
218
+
219
+ # Marks this record to be destroyed as part of the parents save transaction.
220
+ # This does _not_ actually destroy the record instantly, rather child record will be destroyed
221
+ # when <tt>parent.save</tt> is called.
222
+ #
223
+ # Only useful if the <tt>:autosave</tt> option on the parent is enabled for this associated model.
224
+ def mark_for_destruction
225
+ @marked_for_destruction = true
226
+ end
227
+
228
+ # Returns whether or not this record will be destroyed as part of the parents save transaction.
229
+ #
230
+ # Only useful if the <tt>:autosave</tt> option on the parent is enabled for this associated model.
231
+ attr_reader :marked_for_destruction
232
+ alias_method :marked_for_destruction?, :marked_for_destruction
233
+
234
+ # Records the association that is being destroyed and destroying this
235
+ # record in the process.
236
+ attr_writer :destroyed_by_association
237
+
238
+ # Returns the association for the parent being destroyed.
239
+ #
240
+ # Used to avoid updating the counter cache unnecessarily.
241
+ attr_reader :destroyed_by_association
242
+
243
+ # Returns whether or not this record has been changed in any way (including whether
244
+ # any of its nested autosave associations are likewise changed)
245
+ def changed_for_autosave?
246
+ new_record? || changed? || marked_for_destruction? || nested_records_changed_for_autosave?
247
+ end
248
+
249
+ private
250
+
251
+ # Returns the record for an association collection that should be validated
252
+ # or saved. If +autosave+ is +false+ only new records will be returned,
253
+ # unless the parent is/was a new record itself.
254
+ def associated_records_to_validate_or_save(association, new_record, autosave)
255
+ if new_record
256
+ association && association.target
257
+ elsif autosave
258
+ association.target.select { |record| record.changed_for_autosave? }
259
+ else
260
+ association.target.select { |record| record.new_record? }
261
+ end
262
+ end
263
+
264
+ # go through nested autosave associations that are loaded in memory (without loading
265
+ # any new ones), and return true if is changed for autosave
266
+ def nested_records_changed_for_autosave?
267
+ self.class.reflect_on_all_autosave_associations.any? do |reflection|
268
+ association = association_instance_get(reflection.name)
269
+ association && Array.wrap(association.target).any? { |a| a.changed_for_autosave? }
270
+ end
271
+ end
272
+
273
+ # Validate the association if <tt>:validate</tt> or <tt>:autosave</tt> is
274
+ # turned on for the association.
275
+ def validate_single_association(reflection)
276
+ association = association_instance_get(reflection.name)
277
+ record = association && association.reader
278
+ association_valid?(reflection, record) if record
279
+ end
280
+
281
+ # Validate the associated records if <tt>:validate</tt> or
282
+ # <tt>:autosave</tt> is turned on for the association specified by
283
+ # +reflection+.
284
+ def validate_collection_association(reflection)
285
+ if (association = association_instance_get(reflection.name))
286
+ records = associated_records_to_validate_or_save(association,
287
+ new_record?,
288
+ reflection.options[:autosave])
289
+ if records
290
+ records.each { |record| association_valid?(reflection, record) }
291
+ end
292
+ end
293
+ end
294
+
295
+ # Returns whether or not the association is valid and applies any errors to
296
+ # the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
297
+ # enabled records if they're marked_for_destruction? or destroyed.
298
+ def association_valid?(reflection, record)
299
+ return true if record.destroyed? || record.marked_for_destruction?
300
+
301
+ unless valid = record.valid?
302
+ if reflection.options[:autosave]
303
+ record.errors.each do |attribute, message|
304
+ attribute = "#{reflection.name}.#{attribute}"
305
+ errors[attribute] << message
306
+ errors[attribute].uniq!
307
+ end
308
+ else
309
+ errors.add(reflection.name)
310
+ end
311
+ end
312
+ valid
313
+ end
314
+
315
+ # Is used as a before_save callback to check while saving a collection
316
+ # association whether or not the parent was a new record before saving.
317
+ def before_save_collection_association
318
+ @new_record_before_save = new_record?
319
+ true
320
+ end
321
+
322
+ # Saves any new associated records, or all loaded autosave associations if
323
+ # <tt>:autosave</tt> is enabled on the association.
324
+ #
325
+ # In addition, it destroys all children that were marked for destruction
326
+ # with mark_for_destruction.
327
+ def save_collection_association(reflection)
328
+ if association = association_instance_get(reflection.name)
329
+ autosave = reflection.options[:autosave]
330
+
331
+ if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
332
+
333
+ if autosave
334
+ records_to_destroy = records.select(&:marked_for_destruction?)
335
+ records_to_destroy.each { |record| association.destroy(record) }
336
+ records -= records_to_destroy
337
+ end
338
+
339
+ records.each do |record|
340
+ next if record.destroyed?
341
+
342
+ saved = true
343
+
344
+ if autosave != false && (@new_record_before_save || record.new_record?)
345
+ if autosave
346
+ saved = association.insert_record(record, false)
347
+ else
348
+ association.insert_record(record) unless reflection.nested?
349
+ end
350
+ elsif autosave
351
+ saved = record.save(validate: false)
352
+ end
353
+
354
+ fail ActiveRecord::Rollback unless saved
355
+ end
356
+ @new_record_before_save = false unless reflection.macro == :has_and_belongs_to_many
357
+ end
358
+
359
+ # reconstruct the scope now that we know the owner's id
360
+ association.reset_scope if association.respond_to?(:reset_scope)
361
+ end
362
+ end
363
+
364
+ # Saves the associated record if it's new or <tt>:autosave</tt> is enabled
365
+ # on the association.
366
+ #
367
+ # In addition, it will destroy the association if it was marked for
368
+ # destruction with mark_for_destruction.
369
+ #
370
+ # This all happens inside a transaction, _if_ the Transactions module is included into
371
+ # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
372
+ def save_has_one_association(reflection)
373
+ association = association_instance_get(reflection.name)
374
+ record = association && association.load_target
375
+ if record && !record.destroyed?
376
+ autosave = reflection.options[:autosave]
377
+
378
+ if autosave && record.marked_for_destruction?
379
+ record.destroy
380
+ elsif autosave != false
381
+ key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id
382
+
383
+ if (autosave && record.changed_for_autosave?) || new_record? || record_changed?(reflection, record, key)
384
+ unless reflection.through_reflection
385
+ record[reflection.foreign_key] = key
386
+ end
387
+
388
+ saved = record.save(validate: !autosave)
389
+ fail ActiveRecord::Rollback if !saved && autosave
390
+ saved
391
+ end
392
+ end
393
+ end
394
+ end
395
+
396
+ # If the record is new or it has changed, returns true.
397
+ def record_changed?(reflection, record, key)
398
+ record.new_record? ||
399
+ record[reflection.foreign_key] != key ||
400
+ record.changed_attributes.include?(reflection.foreign_key)
401
+ end
402
+
403
+ # Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
404
+ #
405
+ # In addition, it will destroy the association if it was marked for destruction.
406
+ def save_belongs_to_association(reflection)
407
+ association = association_instance_get(reflection.name)
408
+ record = association && association.load_target
409
+ if record && !record.destroyed?
410
+ autosave = reflection.options[:autosave]
411
+
412
+ if autosave && record.marked_for_destruction?
413
+ self[reflection.foreign_key] = nil
414
+ record.destroy
415
+ elsif autosave != false
416
+ saved = record.save(validate: !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
417
+
418
+ if association.updated?
419
+ association_id = record.send(reflection.options[:primary_key] || :id)
420
+ self[reflection.foreign_key] = association_id
421
+ association.loaded!
422
+ end
423
+
424
+ saved if autosave
425
+ end
426
+ end
427
+ end
428
+ end
429
+ end
@@ -360,6 +360,7 @@ module DatastaxRails #:nodoc:
360
360
  include Callbacks
361
361
  include Reflection
362
362
  include Associations
363
+ include AutosaveAssociation
363
364
  include Scoping
364
365
  include Timestamps
365
366
  include Serialization
@@ -387,6 +388,10 @@ module DatastaxRails #:nodoc:
387
388
  # See {DatastaxRails::WideStorageModel} or {DatastaxRails::Payload} model for an example
388
389
  class_attribute :create_options
389
390
 
391
+ # Allows the setting of how frequently to commit data to solr. Default is 10s.
392
+ class_attribute :solr_commit_time
393
+ self.solr_commit_time = 10_000
394
+
390
395
  # Stores the attribute that wide models should cluster on. Basically, this is the
391
396
  # attribute that CQL uses to "group" columns into logical records even though they
392
397
  # are stored on the same row.
@@ -401,9 +406,6 @@ module DatastaxRails #:nodoc:
401
406
  class_attribute :serialized_attributes
402
407
  self.serialized_attributes = {}
403
408
 
404
- # Whether or not we are using solr legacy mappings
405
- class_attribute :legacy_mapping
406
-
407
409
  def initialize(attributes = {}, _options = {})
408
410
  defaults = self.class.column_defaults.dup
409
411
  defaults.each { |_k, v| v.duplicable? ? v.dup : v }
@@ -556,10 +558,6 @@ module DatastaxRails #:nodoc:
556
558
  ancestors.include?(DatastaxRails::WideStorageModel)
557
559
  end
558
560
 
559
- def legacy_mapping?
560
- legacy_mapping
561
- end
562
-
563
561
  def base_class
564
562
  klass = self
565
563
  klass = klass.superclass while klass.superclass != Base
@@ -15,11 +15,12 @@ module DatastaxRails
15
15
  class_attribute :solr
16
16
  end
17
17
 
18
- module ClassMethods
18
+ module ClassMethods # rubocop:disable Style/Documentation
19
19
  DEFAULT_OPTIONS = {
20
- servers: '127.0.0.1:9160',
21
- thrift: {},
22
- cql_version: '3.0.0'
20
+ servers: '127.0.0.1',
21
+ port: 9160,
22
+ connection_options: { timeout: 10 },
23
+ ssl: false
23
24
  }
24
25
 
25
26
  # Returns the current server that we are talking to. This is useful when you are talking to a
@@ -40,6 +41,11 @@ module DatastaxRails
40
41
  #
41
42
  # servers: ["10.1.2.5"]
42
43
  # port: 9042
44
+ # ssl:
45
+ # cert: config/datastax_rails.crt
46
+ # key: config/datastax_rails.key
47
+ # ca_cert: config/ca.crt
48
+ # keypass: changeme
43
49
  # keyspace: "datastax_rails_production"
44
50
  # strategy_class: "org.apache.cassandra.locator.NetworkTopologyStrategy"
45
51
  # strategy_options: {"DS1": "3", "DS2": "3", "DS3": "3"}
@@ -48,18 +54,13 @@ module DatastaxRails
48
54
  # solr:
49
55
  # port: 8983
50
56
  # path: /solr
51
- # ssl:
52
- # use_ssl: true
53
- # cert: config/datastax_rails.crt
54
- # key: config/datastax_rails.key
55
- # keypass: changeme
56
57
  #
57
58
  # The +servers+ entry should be a list of all seed nodes for servers you wish to connect to. DSR
58
59
  # will automatically connect to all nodes in the cluster or in the datacenter if you are using multiple
59
60
  # datacenters. You can safely just list all nodes in a particular datacenter if you would like.
60
61
  #
61
- # The port to connect to, this port will be used for all nodes. Because the `system.peers` table does not contain
62
- # the port that the nodes are listening on, the port must be the same for all nodes.
62
+ # The port to connect to, this port will be used for all nodes. Because the `system.peers` table does
63
+ # not contain the port that the nodes are listening on, the port must be the same for all nodes.
63
64
  #
64
65
  # Since we're using the NetworkTopologyStrategy for our locator, it is important that you configure
65
66
  # cassandra-topology.properties. See the DSE documentation at http://www.datastax.com for more
@@ -73,36 +74,59 @@ module DatastaxRails
73
74
  # * *retries* - Number of times a request will be retried. Should likely be the number of servers - 1.
74
75
  # Defaults to 0.
75
76
  # * *server_retry_period* - Amount of time to wait before retrying a down server. Defaults to 1.
76
- # * *server_max_requests* - Number of requests to make to a server before moving to the next one (helps keep load
77
- # balanced). Default to nil which means cycling does not take place.
77
+ # * *server_max_requests* - Number of requests to make to a server before moving to the next one (helps
78
+ # keep load balanced). Default to nil which means cycling does not take place.
78
79
  # * *retry_overrides* - Overrides retries option for individual exceptions.
79
80
  # * *connect_timeout* - The connection timeout on the Thrift socket. Defaults to 0.1.
80
81
  # * *timeout* - The timeout for the transport layer. Defaults to 1.
81
- # * *timeout_overrides* - Overrides the timeout value for specific methods (advanced).
82
- # * *exception_classes* - List of exceptions for which Thrift will automatically retry a new server in the cluster
83
- # (up to retry limit).
84
- # Defaults to [IOError, Thrift::Exception, Thrift::ApplicationException, Thrift::TransportException].
85
- # * *exception_class_overrides* - List of exceptions which will never cause a retry.
86
- # Defaults to [CassandraCQL::Thrift::InvalidRequestException].
87
- # * *wrapped_exception_options* - List of exceptions that will be automatically wrapped in an exception provided
88
- # by client class with the same name (advanced).
89
- # Defaults to [Thrift::ApplicationException, Thrift::TransportException].
90
- # * *raise* - Whether to raise exceptions or default calls that cause an error (advanced). Defaults to true
91
- # (raise exceptions).
92
- # * *defaults* - When raise is false and an error is encountered, these methods are called to default the return
93
- # value (advanced). Should be a hash of method names to values.
94
- # * *protocol* - The thrift protocol to use (advanced). Defaults to Thrift::BinaryProtocol.
95
- # * *protocol_extra_params* - Any extra parameters to send to the protocol (advanced).
96
- # * *transport* - The thrift transport to use (advanced). Defaults to Thrift::Socket.
97
- # * *transport_wrapper* - The thrift transport wrapper to use (advanced). Defaults to Thrift::FramedTransport.
98
82
  #
99
83
  # See +solr_connection+ for a description of the solr options in datastax.yml
100
84
  def establish_connection(spec)
101
85
  DatastaxRails::Base.config = spec.with_indifferent_access
102
86
  spec.reverse_merge!(DEFAULT_OPTIONS)
103
- self.connection = ::Cql::Client.connect(hosts: spec[:servers],
104
- keyspace: spec[:keyspace],
105
- connection_timeout: spec[:connection_options][:timeout])
87
+ cql_options = { hosts: spec[:servers],
88
+ keyspace: spec[:keyspace],
89
+ connection_timeout: spec[:connection_options][:timeout] }
90
+ if ssl_type
91
+ ca_cert = Pathname.new(DatastaxRails::Base.config[:ssl][:ca_cert])
92
+ ca_cert = Rails.root.join(ca_cert) unless ca_cert.absolute?
93
+ ssl_context = OpenSSL::SSL::SSLContext.new
94
+ ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
95
+ ssl_context.ca_file = ca_cert.to_s
96
+ if ssl_type == :two_way_ssl
97
+ cert = Pathname.new(DatastaxRails::Base.config[:ssl][:cert])
98
+ key = Pathname.new(DatastaxRails::Base.config[:ssl][:key])
99
+ pass = DatastaxRails::Base.config[:ssl][:keypass]
100
+ cert = Rails.root.join(cert) unless cert.absolute?
101
+ key = Rails.root.join(key) unless key.absolute?
102
+ if pass
103
+ ssl_context.key = OpenSSL::PKey::RSA.new(key.read, pass)
104
+ else
105
+ ssl_context.key = OpenSSL::PKey::RSA.new(key.read)
106
+ end
107
+ ssl_context.cert = OpenSSL::X509::Certificate.new(cert.read)
108
+ end
109
+ cql_options[:ssl] = ssl_context
110
+ end
111
+ self.connection = ::Cql::Client.connect(cql_options)
112
+ end
113
+
114
+ def ssl_type
115
+ return false unless DatastaxRails::Base.config && DatastaxRails::Base.config[:ssl]
116
+ config = DatastaxRails::Base.config
117
+ if config[:ssl][:key] && config[:ssl][:cert]
118
+ :two_way_ssl
119
+ elsif config[:ssl][:ca_cert]
120
+ :one_way_ssl
121
+ else
122
+ false
123
+ end
124
+ end
125
+
126
+ def reconnect
127
+ connection.close rescue true
128
+ self.connection = nil
129
+ establish_connection(DatastaxRails::Base.config)
106
130
  end
107
131
 
108
132
  # Returns the base portion of the URL for connecting to SOLR based on the current Cassandra server.
@@ -112,7 +136,7 @@ module DatastaxRails
112
136
  DatastaxRails::Base.establish_connection unless connection
113
137
  port = DatastaxRails::Base.config[:solr][:port]
114
138
  path = DatastaxRails::Base.config[:solr][:path]
115
- protocol = DatastaxRails::Base.config[:solr].key?(:ssl) && DatastaxRails::Base.config[:solr][:ssl][:use_ssl] ? 'https' : 'http' # rubocop:disable LineLength
139
+ protocol = ssl_type ? 'https' : 'http'
116
140
  "#{protocol}://#{current_server}:#{port}#{path}"
117
141
  end
118
142
 
@@ -135,17 +159,18 @@ module DatastaxRails
135
159
  # @return [RSolr::Client] RSolr client object
136
160
  def establish_solr_connection
137
161
  opts = { url: "#{solr_base_url}/#{DatastaxRails::Base.connection.keyspace}.#{column_family}" }
138
- if DatastaxRails::Base.config[:solr].key?(:ssl) &&
139
- DatastaxRails::Base.config[:solr][:ssl].key?(:cert) &&
140
- DatastaxRails::Base.config[:solr][:ssl][:use_ssl]
141
- cert = Pathname.new(DatastaxRails::Base.config[:solr][:ssl][:cert])
142
- key = Pathname.new(DatastaxRails::Base.config[:solr][:ssl][:key])
143
- pass = DatastaxRails::Base.config[:solr][:ssl][:keypass]
162
+ if ssl_type == :two_way_ssl
163
+ ca_cert = Pathname.new(DatastaxRails::Base.config[:ssl][:ca_cert])
164
+ cert = Pathname.new(DatastaxRails::Base.config[:ssl][:cert])
165
+ key = Pathname.new(DatastaxRails::Base.config[:ssl][:key])
166
+ pass = DatastaxRails::Base.config[:ssl][:keypass]
167
+ ca_cert = Rails.root.join(ca_cert) unless ca_cert.absolute?
144
168
  cert = Rails.root.join(cert) unless cert.absolute?
145
169
  key = Rails.root.join(key) unless key.absolute?
146
170
  opts[:ssl_cert_file] = cert.to_s
147
171
  opts[:ssl_key_file] = key.to_s
148
172
  opts[:ssl_key_pass] = pass if pass
173
+ opts[:ssl_ca_file] = ca_cert.to_s
149
174
 
150
175
  RSolr::ClientCert.connect opts
151
176
  else
@@ -1,4 +1,8 @@
1
1
  module DatastaxRails
2
+ # The Cql classes handle all of the generation of CQL. They are constructed in such a way that
3
+ # the statement can be built up over multiple calls before generating the actual CQL.
4
+ #
5
+ # TODO: Add examples
2
6
  module Cql
3
7
  extend ActiveSupport::Autoload
4
8
  class << self
@@ -27,11 +27,23 @@ module DatastaxRails
27
27
  puts cql if ENV['DEBUG_CQL'] == 'true'
28
28
  pp @values if ENV['DEBUG_CQL'] == 'true'
29
29
  digest = Digest::MD5.digest cql
30
- stmt = DatastaxRails::Base.statement_cache[digest] ||= DatastaxRails::Base.connection.prepare(cql)
31
- if @consistency
32
- stmt.execute(*@values, consistency: @consistency)
33
- else
34
- stmt.execute(*@values)
30
+ try_again = true
31
+ begin
32
+ stmt = DatastaxRails::Base.statement_cache[digest] ||= DatastaxRails::Base.connection.prepare(cql)
33
+ if @consistency
34
+ stmt.execute(*@values, consistency: @consistency)
35
+ else
36
+ stmt.execute(*@values)
37
+ end
38
+ rescue ::Cql::NotConnectedError
39
+ if try_again
40
+ Rails.logger.warn('Lost connection to Cassandra. Attempting to reconnect...')
41
+ try_again = false
42
+ DatastaxRails::Base.reconnect
43
+ retry
44
+ else
45
+ raise
46
+ end
35
47
  end
36
48
  end
37
49
  end
@@ -0,0 +1,47 @@
1
+ en:
2
+ # Attributes names common to most models
3
+ #attributes:
4
+ #created_at: "Created at"
5
+ #updated_at: "Updated at"
6
+
7
+ # Default error messages
8
+ errors:
9
+ messages:
10
+ taken: "has already been taken"
11
+
12
+ # Active Record models configuration
13
+ activerecord:
14
+ errors:
15
+ messages:
16
+ record_invalid: "Validation failed: %{errors}"
17
+ restrict_dependent_destroy:
18
+ one: "Cannot delete record because a dependent %{record} exists"
19
+ many: "Cannot delete record because dependent %{record} exist"
20
+ # Append your own errors here or at the model/attributes scope.
21
+
22
+ # You can define own errors for models or model attributes.
23
+ # The values :model, :attribute and :value are always available for interpolation.
24
+ #
25
+ # For example,
26
+ # models:
27
+ # user:
28
+ # blank: "This is a custom blank message for %{model}: %{attribute}"
29
+ # attributes:
30
+ # login:
31
+ # blank: "This is a custom blank message for User login"
32
+ # Will define custom blank validation message for User model and
33
+ # custom blank validation message for login attribute of User model.
34
+ #models:
35
+
36
+ # Translate model names. Used in Model.human_name().
37
+ #models:
38
+ # For example,
39
+ # user: "Dude"
40
+ # will translate User model name to "Dude"
41
+
42
+ # Translate model attribute names. Used in Model.human_attribute_name(attribute).
43
+ #attributes:
44
+ # For example,
45
+ # user:
46
+ # login: "Handle"
47
+ # will translate User attribute "login" as "Handle"
@@ -9,8 +9,7 @@ module DatastaxRails
9
9
  alias_method :new_record?, :new_record
10
10
  end
11
11
 
12
- # rubocop:disable Style/Documentation
13
- module ClassMethods
12
+ module ClassMethods # rubocop:disable Style/Documentation
14
13
  # Removes one or more records with corresponding keys. Last parameter can be a hash
15
14
  # specifying the consistency level. The keys should be in the form returned by
16
15
  # +#id_for_update+
@@ -17,6 +17,7 @@ module DatastaxRails
17
17
  self.reflections = {}
18
18
  end
19
19
 
20
+ # rubocop:disable Style/Documentation
20
21
  module ClassMethods
21
22
  def create_reflection(macro, name, options, datastax_rails)
22
23
  klass = options[:through] ? ThroughReflection : AssociationReflection
@@ -280,6 +281,10 @@ module DatastaxRails
280
281
  end
281
282
  end
282
283
 
284
+ def nested?
285
+ false
286
+ end
287
+
283
288
  private
284
289
 
285
290
  def derive_class_name
@@ -352,6 +357,11 @@ module DatastaxRails
352
357
  end
353
358
  end
354
359
 
360
+ # A through association is nested if there would be more than one join table
361
+ def nested?
362
+ chain.length > 2 || through_reflection.macro == :has_and_belongs_to_many
363
+ end
364
+
355
365
  # Consider the following example:
356
366
  #
357
367
  # class Person
@@ -65,7 +65,7 @@ module DatastaxRails
65
65
  say 'Using custom solrconfig file', :subitem
66
66
  solrconfig = Rails.root.join('config', 'solr', "#{model.column_family}-solrconfig.xml").read
67
67
  else
68
- @legacy = model.legacy_mapping?
68
+ @solr_commit_time = model.solr_commit_time
69
69
  solrconfig = ERB.new(File.read(File.join(File.dirname(__FILE__), '..', '..', '..', 'config', 'solrconfig.xml.erb'))).result(binding)
70
70
  end
71
71
  if Rails.root.join('config', 'solr', "#{model.column_family}-stopwords.txt").exist?
@@ -1,4 +1,4 @@
1
1
  # rubocop:disable Style/Documentation
2
2
  module DatastaxRails
3
- VERSION = '2.0.16'
3
+ VERSION = '2.0.18'
4
4
  end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe DatastaxRails::Base do
4
+ describe 'Autosave Associations' do
5
+ describe 'collections' do
6
+ it 'saves child records built via the association' do
7
+ p = Person.new(name: 'Jim')
8
+ c1 = p.cars.build(name: 'Jeep')
9
+ c2 = p.cars.build(name: 'Ford')
10
+ p.save
11
+ Person.commit_solr
12
+ expect(Car.find(c1.id).person).to eq(p)
13
+ expect(Car.find(c2.id).person).to eq(p)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -3,9 +3,8 @@ require 'spec_helper'
3
3
  describe DatastaxRails::Relation do
4
4
  before(:each) do
5
5
  @relation = DatastaxRails::Relation.new(Hobby, 'hobbies')
6
- ('a'..'l').each_with_index do |letter, idx|
6
+ ('a'..'l').each_with_index do |letter, _idx|
7
7
  Hobby.create(name: letter)
8
- sleep(1) if idx % 5 == 4 # Performance hack
9
8
  end
10
9
  Hobby.commit_solr
11
10
  end
@@ -13,7 +12,6 @@ describe DatastaxRails::Relation do
13
12
  %w(cassandra solr).each do |access_method|
14
13
  describe '#find_each' do
15
14
  it "returns each record one at a time with #{access_method}" do
16
- sleep(1)
17
15
  missed_hobbies = ('a'..'l').to_a
18
16
  @relation.send('with_' + access_method).find_each(batch_size: 5) do |hobby|
19
17
  missed_hobbies.delete_if { |h| h == hobby.name }
@@ -0,0 +1,19 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDKDCCApGgAwIBAgIJAIgWnQPYsbWtMA0GCSqGSIb3DQEBBQUAMGwxCzAJBgNV
3
+ BAYTAlVTMREwDwYDVQQIEwhWaXJnaW5pYTEPMA0GA1UEBxMGUmVzdG9uMRswGQYD
4
+ VQQKExJPcGVuIFNvdXJjZSBDZW50ZXIxDTALBgNVBAsTBFNBREUxDTALBgNVBAMT
5
+ BFNBREUwHhcNMTAwODA2MTMzMzA1WhcNMjAwODAzMTMzMzA1WjBsMQswCQYDVQQG
6
+ EwJVUzERMA8GA1UECBMIVmlyZ2luaWExDzANBgNVBAcTBlJlc3RvbjEbMBkGA1UE
7
+ ChMST3BlbiBTb3VyY2UgQ2VudGVyMQ0wCwYDVQQLEwRTQURFMQ0wCwYDVQQDEwRT
8
+ QURFMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDxn2DOHCUMsJoD4MoUEzpW
9
+ 1WFNag+ogebitEL1OwDeUuFuwZJAnOk3Gipw7ye27fU8qb+62BtHjg2EbnRa5NSG
10
+ teS4HnCIt149FzgB06A2E+6s10oX9ehqT1FpALLFEyf/KaP82KPVeDq4ki68LIVJ
11
+ +YrvBbgNpH7a1czlXeVNNQIDAQABo4HRMIHOMB0GA1UdDgQWBBREhQm645qfcEw3
12
+ uNXO6gaROCpSJzCBngYDVR0jBIGWMIGTgBREhQm645qfcEw3uNXO6gaROCpSJ6Fw
13
+ pG4wbDELMAkGA1UEBhMCVVMxETAPBgNVBAgTCFZpcmdpbmlhMQ8wDQYDVQQHEwZS
14
+ ZXN0b24xGzAZBgNVBAoTEk9wZW4gU291cmNlIENlbnRlcjENMAsGA1UECxMEU0FE
15
+ RTENMAsGA1UEAxMEU0FERYIJAIgWnQPYsbWtMAwGA1UdEwQFMAMBAf8wDQYJKoZI
16
+ hvcNAQEFBQADgYEA2ieesbXFL3jG3ebBmQLtHwjcWlQYcVGTq+wjxCBuBcZXEVFn
17
+ Qi6XxbCvRSm0lLWYRHEc80WL3FezfREY43yiewnT24S8oKYlRb2v7Yc2sUjE72Ue
18
+ nV7M6s4CPmqIYGkH+ukbxQVnAE0f1BNIoTsPx9XFr6Yx6kxZ4qSWc2IjuO4=
19
+ -----END CERTIFICATE-----
@@ -12,8 +12,12 @@ productiont:
12
12
  path: /solr
13
13
 
14
14
  development:
15
- servers: ["127.0.0.1"]
15
+ servers: ["sade-jasonk"]
16
16
  port: 9042
17
+ ssl:
18
+ #cert: config/datastax_rails.crt
19
+ #key: config/datastax_rails.key
20
+ #ca_cert: config/ca.crt
17
21
  keyspace: "datastax_rails_development"
18
22
  strategy_class: "org.apache.cassandra.locator.SimpleStrategy"
19
23
  strategy_options: {"replication_factor": "1"}
@@ -24,8 +28,12 @@ development:
24
28
  path: /solr
25
29
 
26
30
  test:
27
- servers: ["127.0.0.1"]
31
+ servers: ["sade-jasonk"]
28
32
  port: 9042
33
+ ssl:
34
+ #cert: config/datastax_rails.crt
35
+ #key: config/datastax_rails.key
36
+ #ca_cert: config/ca.crt
29
37
  keyspace: "datastax_rails_test"
30
38
  strategy_class: "org.apache.cassandra.locator.SimpleStrategy"
31
39
  strategy_options: {"replication_factor": "1"}
@@ -15607,3 +15607,4 @@ Reconnecting and retrying
15607
15607
  Error connecting to database
15608
15608
  the scheme http does not accept registry part: :8983 (or bad hostname?)
15609
15609
  Reconnecting and retrying
15610
+ Lost connection to Cassandra. Attempting to reconnect...
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.16
4
+ version: 2.0.18
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-07-28 00:00:00.000000000 Z
11
+ date: 2014-09-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -202,6 +202,7 @@ files:
202
202
  - lib/datastax_rails/attribute_methods/read.rb
203
203
  - lib/datastax_rails/attribute_methods/typecasting.rb
204
204
  - lib/datastax_rails/attribute_methods/write.rb
205
+ - lib/datastax_rails/autosave_association.rb
205
206
  - lib/datastax_rails/base.rb
206
207
  - lib/datastax_rails/callbacks.rb
207
208
  - lib/datastax_rails/cassandra_only_model.rb
@@ -230,6 +231,7 @@ files:
230
231
  - lib/datastax_rails/errors.rb
231
232
  - lib/datastax_rails/grouped_collection.rb
232
233
  - lib/datastax_rails/inheritance.rb
234
+ - lib/datastax_rails/locale/en.yml
233
235
  - lib/datastax_rails/payload_model.rb
234
236
  - lib/datastax_rails/persistence.rb
235
237
  - lib/datastax_rails/railtie.rb
@@ -272,6 +274,7 @@ files:
272
274
  - spec/datastax_rails/associations_spec.rb
273
275
  - spec/datastax_rails/attribute_methods/typecasting_spec.rb
274
276
  - spec/datastax_rails/attribute_methods_spec.rb
277
+ - spec/datastax_rails/autosave_association_spec.rb
275
278
  - spec/datastax_rails/base_spec.rb
276
279
  - spec/datastax_rails/callbacks_spec.rb
277
280
  - spec/datastax_rails/column_spec.rb
@@ -309,6 +312,7 @@ files:
309
312
  - spec/dummy/config.ru
310
313
  - spec/dummy/config/application.rb
311
314
  - spec/dummy/config/boot.rb
315
+ - spec/dummy/config/ca.crt
312
316
  - spec/dummy/config/database.yml
313
317
  - spec/dummy/config/datastax.yml
314
318
  - spec/dummy/config/datastax_rails.crt
@@ -393,6 +397,7 @@ test_files:
393
397
  - spec/datastax_rails/cql/update_spec.rb
394
398
  - spec/datastax_rails/cql/base_spec.rb
395
399
  - spec/datastax_rails/cql/select_spec.rb
400
+ - spec/datastax_rails/autosave_association_spec.rb
396
401
  - spec/datastax_rails/persistence_spec.rb
397
402
  - spec/datastax_rails/relation/stats_methods_spec.rb
398
403
  - spec/datastax_rails/relation/spawn_methods_spec.rb
@@ -440,6 +445,7 @@ test_files:
440
445
  - spec/dummy/config/application.rb
441
446
  - spec/dummy/config/datastax.yml
442
447
  - spec/dummy/config/routes.rb
448
+ - spec/dummy/config/ca.crt
443
449
  - spec/dummy/config/boot.rb
444
450
  - spec/dummy/config/datastax_rails.crt
445
451
  - spec/dummy/script/rails