datastax_rails 2.0.16 → 2.0.18

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