datastax_rails 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +62 -0
  3. data/Rakefile +34 -0
  4. data/config/schema.xml +266 -0
  5. data/config/schema.xml.erb +70 -0
  6. data/config/solrconfig.xml +1564 -0
  7. data/config/stopwords.txt +58 -0
  8. data/lib/datastax_rails/associations/association.rb +224 -0
  9. data/lib/datastax_rails/associations/association_scope.rb +25 -0
  10. data/lib/datastax_rails/associations/belongs_to_association.rb +64 -0
  11. data/lib/datastax_rails/associations/builder/association.rb +56 -0
  12. data/lib/datastax_rails/associations/builder/belongs_to.rb +30 -0
  13. data/lib/datastax_rails/associations/builder/collection_association.rb +48 -0
  14. data/lib/datastax_rails/associations/builder/has_and_belongs_to_many.rb +36 -0
  15. data/lib/datastax_rails/associations/builder/has_many.rb +54 -0
  16. data/lib/datastax_rails/associations/builder/has_one.rb +52 -0
  17. data/lib/datastax_rails/associations/builder/singular_association.rb +56 -0
  18. data/lib/datastax_rails/associations/collection_association.rb +274 -0
  19. data/lib/datastax_rails/associations/collection_proxy.rb +118 -0
  20. data/lib/datastax_rails/associations/has_and_belongs_to_many_association.rb +44 -0
  21. data/lib/datastax_rails/associations/has_many_association.rb +58 -0
  22. data/lib/datastax_rails/associations/has_one_association.rb +68 -0
  23. data/lib/datastax_rails/associations/singular_association.rb +58 -0
  24. data/lib/datastax_rails/associations.rb +86 -0
  25. data/lib/datastax_rails/attribute_methods/definition.rb +20 -0
  26. data/lib/datastax_rails/attribute_methods/dirty.rb +43 -0
  27. data/lib/datastax_rails/attribute_methods/typecasting.rb +50 -0
  28. data/lib/datastax_rails/attribute_methods.rb +104 -0
  29. data/lib/datastax_rails/base.rb +587 -0
  30. data/lib/datastax_rails/batches.rb +35 -0
  31. data/lib/datastax_rails/callbacks.rb +37 -0
  32. data/lib/datastax_rails/collection.rb +9 -0
  33. data/lib/datastax_rails/connection.rb +21 -0
  34. data/lib/datastax_rails/consistency.rb +33 -0
  35. data/lib/datastax_rails/cql/base.rb +15 -0
  36. data/lib/datastax_rails/cql/column_family.rb +38 -0
  37. data/lib/datastax_rails/cql/consistency.rb +13 -0
  38. data/lib/datastax_rails/cql/create_column_family.rb +63 -0
  39. data/lib/datastax_rails/cql/create_keyspace.rb +30 -0
  40. data/lib/datastax_rails/cql/delete.rb +41 -0
  41. data/lib/datastax_rails/cql/drop_column_family.rb +13 -0
  42. data/lib/datastax_rails/cql/drop_keyspace.rb +13 -0
  43. data/lib/datastax_rails/cql/insert.rb +53 -0
  44. data/lib/datastax_rails/cql/select.rb +51 -0
  45. data/lib/datastax_rails/cql/truncate.rb +13 -0
  46. data/lib/datastax_rails/cql/update.rb +68 -0
  47. data/lib/datastax_rails/cql/use_keyspace.rb +13 -0
  48. data/lib/datastax_rails/cql.rb +25 -0
  49. data/lib/datastax_rails/cursor.rb +90 -0
  50. data/lib/datastax_rails/errors.rb +16 -0
  51. data/lib/datastax_rails/identity/abstract_key_factory.rb +26 -0
  52. data/lib/datastax_rails/identity/custom_key_factory.rb +36 -0
  53. data/lib/datastax_rails/identity/hashed_natural_key_factory.rb +10 -0
  54. data/lib/datastax_rails/identity/natural_key_factory.rb +37 -0
  55. data/lib/datastax_rails/identity/uuid_key_factory.rb +23 -0
  56. data/lib/datastax_rails/identity.rb +53 -0
  57. data/lib/datastax_rails/log_subscriber.rb +37 -0
  58. data/lib/datastax_rails/migrations/migration.rb +15 -0
  59. data/lib/datastax_rails/migrations.rb +36 -0
  60. data/lib/datastax_rails/mocking.rb +15 -0
  61. data/lib/datastax_rails/persistence.rb +133 -0
  62. data/lib/datastax_rails/railtie.rb +20 -0
  63. data/lib/datastax_rails/reflection.rb +472 -0
  64. data/lib/datastax_rails/relation/finder_methods.rb +184 -0
  65. data/lib/datastax_rails/relation/modification_methods.rb +80 -0
  66. data/lib/datastax_rails/relation/search_methods.rb +349 -0
  67. data/lib/datastax_rails/relation/spawn_methods.rb +107 -0
  68. data/lib/datastax_rails/relation.rb +393 -0
  69. data/lib/datastax_rails/schema/migration.rb +106 -0
  70. data/lib/datastax_rails/schema/migration_proxy.rb +25 -0
  71. data/lib/datastax_rails/schema/migrator.rb +212 -0
  72. data/lib/datastax_rails/schema.rb +37 -0
  73. data/lib/datastax_rails/scoping.rb +394 -0
  74. data/lib/datastax_rails/serialization.rb +6 -0
  75. data/lib/datastax_rails/tasks/column_family.rb +162 -0
  76. data/lib/datastax_rails/tasks/ds.rake +63 -0
  77. data/lib/datastax_rails/tasks/keyspace.rb +57 -0
  78. data/lib/datastax_rails/timestamps.rb +19 -0
  79. data/lib/datastax_rails/type.rb +16 -0
  80. data/lib/datastax_rails/types/array_type.rb +77 -0
  81. data/lib/datastax_rails/types/base_type.rb +26 -0
  82. data/lib/datastax_rails/types/binary_type.rb +15 -0
  83. data/lib/datastax_rails/types/boolean_type.rb +22 -0
  84. data/lib/datastax_rails/types/date_type.rb +17 -0
  85. data/lib/datastax_rails/types/float_type.rb +18 -0
  86. data/lib/datastax_rails/types/integer_type.rb +18 -0
  87. data/lib/datastax_rails/types/string_type.rb +16 -0
  88. data/lib/datastax_rails/types/text_type.rb +16 -0
  89. data/lib/datastax_rails/types/time_type.rb +17 -0
  90. data/lib/datastax_rails/types.rb +9 -0
  91. data/lib/datastax_rails/validations/uniqueness.rb +119 -0
  92. data/lib/datastax_rails/validations.rb +48 -0
  93. data/lib/datastax_rails/version.rb +3 -0
  94. data/lib/datastax_rails.rb +87 -0
  95. data/lib/solr_no_escape.rb +28 -0
  96. data/spec/datastax_rails/associations/belongs_to_association_spec.rb +7 -0
  97. data/spec/datastax_rails/associations/has_many_association_spec.rb +37 -0
  98. data/spec/datastax_rails/associations_spec.rb +22 -0
  99. data/spec/datastax_rails/attribute_methods_spec.rb +23 -0
  100. data/spec/datastax_rails/base_spec.rb +15 -0
  101. data/spec/datastax_rails/cql/select_spec.rb +12 -0
  102. data/spec/datastax_rails/cql/update_spec.rb +0 -0
  103. data/spec/datastax_rails/relation/finder_methods_spec.rb +54 -0
  104. data/spec/datastax_rails/relation/modification_methods_spec.rb +41 -0
  105. data/spec/datastax_rails/relation/search_methods_spec.rb +117 -0
  106. data/spec/datastax_rails/relation/spawn_methods_spec.rb +28 -0
  107. data/spec/datastax_rails/relation_spec.rb +130 -0
  108. data/spec/datastax_rails/validations/uniqueness_spec.rb +41 -0
  109. data/spec/datastax_rails_spec.rb +5 -0
  110. data/spec/dummy/Rakefile +8 -0
  111. data/spec/dummy/app/assets/javascripts/application.js +9 -0
  112. data/spec/dummy/app/assets/stylesheets/application.css +7 -0
  113. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  114. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  115. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  116. data/spec/dummy/config/application.rb +47 -0
  117. data/spec/dummy/config/boot.rb +10 -0
  118. data/spec/dummy/config/database.yml +25 -0
  119. data/spec/dummy/config/datastax.yml +18 -0
  120. data/spec/dummy/config/environment.rb +5 -0
  121. data/spec/dummy/config/environments/development.rb +30 -0
  122. data/spec/dummy/config/environments/production.rb +60 -0
  123. data/spec/dummy/config/environments/test.rb +39 -0
  124. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  125. data/spec/dummy/config/initializers/inflections.rb +10 -0
  126. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  127. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  128. data/spec/dummy/config/initializers/session_store.rb +8 -0
  129. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  130. data/spec/dummy/config/locales/en.yml +5 -0
  131. data/spec/dummy/config/routes.rb +58 -0
  132. data/spec/dummy/config/sunspot.yml +17 -0
  133. data/spec/dummy/config.ru +4 -0
  134. data/spec/dummy/ks/migrate/20111117224534_models.rb +20 -0
  135. data/spec/dummy/ks/schema.json +180 -0
  136. data/spec/dummy/log/development.log +298 -0
  137. data/spec/dummy/log/production.log +0 -0
  138. data/spec/dummy/log/test.log +20307 -0
  139. data/spec/dummy/public/404.html +26 -0
  140. data/spec/dummy/public/422.html +26 -0
  141. data/spec/dummy/public/500.html +26 -0
  142. data/spec/dummy/public/favicon.ico +0 -0
  143. data/spec/dummy/script/rails +6 -0
  144. data/spec/spec.opts +5 -0
  145. data/spec/spec_helper.rb +29 -0
  146. data/spec/support/datastax_test_hook.rb +14 -0
  147. data/spec/support/models.rb +72 -0
  148. metadata +353 -0
@@ -0,0 +1,58 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one or more
2
+ # contributor license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright ownership.
4
+ # The ASF licenses this file to You under the Apache License, Version 2.0
5
+ # (the "License"); you may not use this file except in compliance with
6
+ # the License. You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ #-----------------------------------------------------------------------
17
+ # a couple of test stopwords to test that the words are really being
18
+ # configured from this file:
19
+ stopworda
20
+ stopwordb
21
+
22
+ #Standard english stop words taken from Lucene's StopAnalyzer
23
+ a
24
+ an
25
+ and
26
+ are
27
+ as
28
+ at
29
+ be
30
+ but
31
+ by
32
+ for
33
+ if
34
+ in
35
+ into
36
+ is
37
+ it
38
+ no
39
+ not
40
+ of
41
+ on
42
+ or
43
+ s
44
+ such
45
+ t
46
+ that
47
+ the
48
+ their
49
+ then
50
+ there
51
+ these
52
+ they
53
+ this
54
+ to
55
+ was
56
+ will
57
+ with
58
+
@@ -0,0 +1,224 @@
1
+ require 'active_support/core_ext/array/wrap'
2
+ require 'active_support/core_ext/object/inclusion'
3
+
4
+ module DatastaxRails
5
+ module Associations
6
+ # = DatastaxRails Associations
7
+ #
8
+ # This is the root class of all associations ('+ Foo' signifies an included module Foo):
9
+ #
10
+ # Association
11
+ # SingularAssociation
12
+ # HasOneAssociation
13
+ # HasOneThroughAssociation + ThroughAssociation (Not implemented)
14
+ # BelongsToAssociation
15
+ # BelongsToPolymorphicAssociation (Not implemented)
16
+ # CollectionAssociation
17
+ # HasAndBelongsToManyAssociation (Not implemented)
18
+ # HasManyAssociation
19
+ # HasManyThroughAssociation + ThroughAssociation (Not implemented)
20
+ class Association #:nodoc:
21
+ attr_reader :owner, :target, :reflection
22
+
23
+ delegate :options, :to => :reflection
24
+
25
+ def initialize(owner, reflection)
26
+ reflection.check_validity!
27
+
28
+ @target = nil
29
+ @owner, @reflection = owner, reflection
30
+ @updated = false
31
+
32
+ reset
33
+ reset_scope
34
+ end
35
+
36
+ # Returns the name of the column family name of the related class:
37
+ #
38
+ # post.comments.aliased_column_family # => "comments"
39
+ #
40
+ def aliased_column_family
41
+ reflection.klass.column_family
42
+ end
43
+
44
+ # Resets the \loaded flag to +false+ and sets the \target to +nil+.
45
+ def reset
46
+ @loaded = false
47
+ @target = nil
48
+ end
49
+
50
+ # Reloads the \target and returns +self+ on success.
51
+ def reload
52
+ reset
53
+ reset_scope
54
+ load_target
55
+ self unless target.nil?
56
+ end
57
+
58
+ # Has the \target been already \loaded?
59
+ def loaded?
60
+ @loaded
61
+ end
62
+
63
+ # Asserts the \target has been loaded setting the \loaded flag to +true+.
64
+ def loaded!
65
+ @loaded = true
66
+ @stale_state = stale_state
67
+ end
68
+
69
+ # The target is stale if the target no longer points to the record(s) that the
70
+ # relevant foreign_key(s) refers to. If stale, the association accessor method
71
+ # on the owner will reload the target. It's up to subclasses to implement the
72
+ # stale_state method if relevant.
73
+ #
74
+ # Note that if the target has not been loaded, it is not considered stale.
75
+ def stale_target?
76
+ loaded? && @stale_state != stale_state
77
+ end
78
+
79
+ # Sets the target of this association to <tt>\target</tt>, and the \loaded flag to +true+.
80
+ def target=(target)
81
+ @target = target
82
+ loaded!
83
+ end
84
+
85
+ def scoped
86
+ target_scope.merge(association_scope)
87
+ end
88
+
89
+ # The scope for this association.
90
+ #
91
+ # Note that the association_scope is merged into the target_scope only when the
92
+ # scoped method is called. This is because at that point the call may be surrounded
93
+ # by scope.scoping { ... } or with_scope { ... } etc, which affects the scope which
94
+ # actually gets built.
95
+ def association_scope
96
+ if klass
97
+ @association_scope ||= AssociationScope.new(self).scope
98
+ end
99
+ end
100
+
101
+ def reset_scope
102
+ @association_scope = nil
103
+ end
104
+
105
+ # Set the inverse association, if possible
106
+ def set_inverse_instance(record)
107
+ if record && invertible_for?(record)
108
+ inverse = record.association(inverse_reflection_for(record).name)
109
+ inverse.target = owner
110
+ end
111
+ end
112
+
113
+ # This class of the target. belongs_to polymorphic overrides this to look at the
114
+ # polymorphic_type field on the owner.
115
+ def klass
116
+ reflection.klass
117
+ end
118
+
119
+ # Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
120
+ # through association's scope)
121
+ def target_scope
122
+ klass.scoped
123
+ end
124
+
125
+ # Loads the \target if needed and returns it.
126
+ #
127
+ # This method is abstract in the sense that it relies on +find_target+,
128
+ # which is expected to be provided by descendants.
129
+ #
130
+ # If the \target is already \loaded it is just returned. Thus, you can call
131
+ # +load_target+ unconditionally to get the \target.
132
+ #
133
+ # DatastaxRails::RecordNotFound is rescued within the method, and it is
134
+ # not reraised. The proxy is \reset and +nil+ is the return value.
135
+ def load_target
136
+ if find_target?
137
+ @target ||= find_target
138
+ end
139
+ loaded! unless loaded?
140
+ target
141
+ rescue DatastaxRails::RecordNotFound
142
+ reset
143
+ end
144
+
145
+ private
146
+ def find_target?
147
+ !loaded? && (!owner.new_record? || foreign_key_present?) && klass
148
+ end
149
+
150
+ def creation_attributes
151
+ {}.tap do |attributes|
152
+ if reflection.macro.in?([:has_one, :has_many]) && !options[:through]
153
+ attributes[reflection.foreign_key] = owner.id
154
+
155
+ # Note, polymorphic relationships are not implemented yet
156
+ if reflection.options[:as]
157
+ attributes[reflection.type] = owner.class.base_class.name
158
+ end
159
+ end
160
+ end
161
+ end
162
+
163
+ # Sets the owner attributes on the given record
164
+ def set_owner_attributes(record)
165
+ creation_attributes.each { |key, value| record[key] = value }
166
+ end
167
+
168
+ # Should be true if there is a foreign key present on the owner which
169
+ # references the target. This is used to determine whether we can load
170
+ # the target if the owner is currently a new record (and therefore
171
+ # without a key).
172
+ #
173
+ # Currently implemented by belongs_to (vanilla and polymorphic) and
174
+ # has_one/has_many :through associations which go through a belongs_to
175
+ def foreign_key_present?
176
+ false
177
+ end
178
+
179
+ # Raises DatastaxRails::AssociationTypeMismatch unless +record+ is of
180
+ # the kind of the class of the associated objects. Meant to be used as
181
+ # a sanity check when you are about to assign an associated record.
182
+ def raise_on_type_mismatch(record)
183
+ unless record.is_a?(reflection.klass) || record.is_a?(reflection.class_name.constantize)
184
+ message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
185
+ raise DatastaxRails::AssociationTypeMismatch, message
186
+ end
187
+ end
188
+
189
+ # Can be redefined by subclasses, notably polymorphic belongs_to
190
+ # The record parameter is necessary to support polymorphic inverses as we must check for
191
+ # the association in the specific class of the record.
192
+ def inverse_reflection_for(record)
193
+ reflection.inverse_of
194
+ end
195
+
196
+ # Is this association invertible? Can be redefined by subclasses.
197
+ def invertible_for?(record)
198
+ inverse_reflection_for(record)
199
+ end
200
+
201
+ # This should be implemented to return the values of the relevant key(s) on the owner,
202
+ # so that when state_state is different from the value stored on the last find_target,
203
+ # the target is stale.
204
+ #
205
+ # This is only relevant to certain associations, which is why it returns nil by default.
206
+ def stale_state
207
+ nil
208
+ end
209
+
210
+ def association_class
211
+ @reflection.klass
212
+ end
213
+
214
+ def build_record(attributes, options)
215
+ reflection.build_association(attributes, options) do |r|
216
+ r.assign_attributes(
217
+ create_scope.except(*r.changed),
218
+ :without_protection => true
219
+ )
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,25 @@
1
+ module DatastaxRails
2
+ module Associations
3
+ class AssociationScope #:nodoc:
4
+ attr_reader :association
5
+
6
+ delegate :klass, :owner, :reflection, :to => :association
7
+ delegate :chain, :options, :datastax_rails, :to => :reflection
8
+
9
+ def initialize(association)
10
+ @association = association
11
+ end
12
+
13
+ def scope
14
+ scope = klass.unscoped
15
+ scope = scope.extending(*Array.wrap(options[:extend]))
16
+
17
+ if reflection.source_macro == :belongs_to
18
+ scope.where('id' => owner.send(reflection.foreign_key))
19
+ else
20
+ scope.where(reflection.foreign_key => owner.id)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,64 @@
1
+ module DatastaxRails
2
+ module Associations
3
+ class BelongsToAssociation < SingularAssociation #:nodoc:
4
+ def replace(record)
5
+ raise_on_type_mismatch(record) if record
6
+
7
+ replace_keys(record)
8
+ set_inverse_instance(record)
9
+
10
+ @updated = true if record
11
+
12
+ self.target = record
13
+ end
14
+
15
+ def updated?
16
+ @updated
17
+ end
18
+
19
+ private
20
+
21
+ def find_target?
22
+ !loaded? && foreign_key_present? && klass
23
+ end
24
+
25
+ # Checks whether record is different to the current target, without loading it
26
+ def different_target?(record)
27
+ record.nil? && owner[reflection.foreign_key] ||
28
+ record && record.id != owner[reflection.foreign_key]
29
+ end
30
+
31
+ def replace_keys(record)
32
+ owner.send("#{reflection.foreign_key}_will_change!")
33
+ if record
34
+ owner[reflection.foreign_key] = record.id
35
+ else
36
+ owner[reflection.foreign_key] = nil
37
+ end
38
+ end
39
+
40
+ def foreign_key_present?
41
+ owner[reflection.foreign_key]
42
+ end
43
+
44
+ # NOTE - for now, we're only supporting inverse setting from belongs_to back onto
45
+ # has_one associations.
46
+ def invertible_for?(record)
47
+ inverse = inverse_reflection_for(record)
48
+ inverse && inverse.macro == :has_one
49
+ end
50
+
51
+ def target_id
52
+ if options[:primary_key]
53
+ owner.send(reflection.name).try(:id)
54
+ else
55
+ owner[reflection.foreign_key]
56
+ end
57
+ end
58
+
59
+ def stale_state
60
+ owner[reflection.foreign_key].to_s
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,56 @@
1
+ module DatastaxRails::Associations::Builder
2
+ class Association #:nodoc:
3
+ class_attribute :valid_options
4
+ self.valid_options = [:class_name, :foreign_key]
5
+
6
+ # Set by subclasses
7
+ class_attribute :macro
8
+
9
+ attr_reader :model, :name, :options, :reflection
10
+
11
+ def self.build(model, name, options)
12
+ new(model, name, options).build
13
+ end
14
+
15
+ def initialize(model, name, options)
16
+ @model, @name, @options = model, name, options
17
+ end
18
+
19
+ def build
20
+ validate_options
21
+ reflection = model.create_reflection(self.class.macro, name, options, model)
22
+ define_accessors
23
+ reflection
24
+ end
25
+
26
+ def mixin
27
+ @model.generated_attribute_methods
28
+ end
29
+
30
+ private
31
+ def validate_options
32
+ options.assert_valid_keys(self.class.valid_options)
33
+ end
34
+
35
+ def define_accessors
36
+ define_readers
37
+ define_writers
38
+ end
39
+
40
+ def define_readers
41
+ name = self.name
42
+
43
+ model.redefine_method(name) do |*params|
44
+ association(name).reader(*params)
45
+ end
46
+ end
47
+
48
+ def define_writers
49
+ name = self.name
50
+
51
+ model.redefine_method("#{name}=") do |value|
52
+ association(name).writer(value)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,30 @@
1
+ module DatastaxRails::Associations::Builder
2
+ class BelongsTo < SingularAssociation
3
+ self.macro = :belongs_to
4
+
5
+ def build
6
+ reflection = super
7
+ configure_dependency
8
+ reflection
9
+ end
10
+
11
+ private
12
+
13
+ def configure_dependency
14
+ if options[:dependent]
15
+ unless options[:dependent].in?([:destroy, :delete])
16
+ raise ArgumentError, "The :dependent option expects either :destroy or :delete (#{options[:dependent].inspect})"
17
+ end
18
+
19
+ method_name = "belongs_to_dependent_#{options[:dependent]}_for_#{name}"
20
+ model.send(:class_eval, <<-eoruby, __FILE__, __LINE__ + 1)
21
+ def #{method_name}
22
+ association = #{name}
23
+ association.#{options[:dependent]} if association
24
+ end
25
+ eoruby
26
+ model.after_destroy method_name
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,48 @@
1
+ module DatastaxRails::Associations::Builder #:nodoc:
2
+ class CollectionAssociation < Association #:nodoc:
3
+ CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
4
+
5
+ self.valid_options += [:columm_family, :order, :uniq, :before_add, :before_remove, :after_add, :after_remove]
6
+
7
+ def self.build(model, name, options)
8
+ new(model, name, options).build
9
+ end
10
+
11
+ def build
12
+ reflection = super
13
+ CALLBACKS.each { |callback_name| define_callback(callback_name) }
14
+ end
15
+
16
+ def writable?
17
+ true
18
+ end
19
+
20
+ protected
21
+
22
+ def define_callback(callback_name)
23
+ full_callback_name = "#{callback_name}_for_#{name}"
24
+
25
+ # XXX : why do i need method_defined? I think its because of the inheritance chain
26
+ model.class_attribute full_callback_name.to_sym unless model.method_defined?(full_callback_name)
27
+ model.send("#{full_callback_name}=", Array.wrap(options[callback_name.to_sym]))
28
+ end
29
+
30
+ def define_readers
31
+ super
32
+
33
+ name = self.name
34
+ mixin.redefine_method("#{name.to_s.singularize}_ids") do
35
+ association(name).ids_reader
36
+ end
37
+ end
38
+
39
+ def define_writers
40
+ super
41
+
42
+ name = self.name
43
+ mixin.redefine_method("#{name.to_s.singularize}_ids=") do |ids|
44
+ association(name).ids_writer(ids)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,36 @@
1
+ module DatastaxRails::Associations::Builder #:nodoc:
2
+ class HasAndBelongsToMany < CollectionAssociation #:nodoc:
3
+ self.macro = :has_and_belongs_to_many
4
+
5
+ def build
6
+ reflection = super
7
+ define_destroy_hook
8
+ check_for_join_column_family
9
+ reflection
10
+ end
11
+
12
+ private
13
+ def define_destroy_hook
14
+ name = self.name
15
+ model.send(:include, Module.new {
16
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
17
+ def destroy_associations
18
+ association(#{name.to_sym.inspect}).delete_all_on_destroy
19
+ super
20
+ end
21
+ RUBY
22
+ })
23
+ end
24
+
25
+ def check_for_join_column_family
26
+ unless DatastaxRails::Base.connection.column_families.has_key?("many_to_many_joins")
27
+ cf = Cassandra::ColumnFamily.new
28
+ cf.name = "many_to_many_joins"
29
+ cf.keyspace = DatastaxRails::Base.connection.keyspace
30
+ cf.comparator_type = 'BytesType'
31
+ cf.column_type = 'Standard'
32
+ DatastaxRails::Base.connection.add_column_family(cf)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,54 @@
1
+ module DatastaxRails::Associations::Builder
2
+ class HasMany < CollectionAssociation #:nodoc:
3
+ self.macro = :has_many
4
+
5
+ self.valid_options += [:primary_key, :dependent, :source_type]
6
+
7
+ def build
8
+ reflection = super
9
+ configure_dependency
10
+ reflection
11
+ end
12
+
13
+ private
14
+
15
+ def configure_dependency
16
+ if options[:dependent]
17
+ unless options[:dependent].in?([:destroy]) # Only destroy and restrict supported for now
18
+ raise ArgumentError, "The :dependent option only handles :destroy or :restrict for now (#{options[:dependent].inspect})"
19
+ end
20
+
21
+ send("define_#{options[:dependent]}_dependency_method")
22
+ model.before_destroy dependency_method_name
23
+ end
24
+ end
25
+
26
+ def define_destroy_dependency_method
27
+ name = self.name
28
+ mixin.redefine_method(dependency_method_name) do
29
+ send(name).each do |o|
30
+ # No point in executing the counter update since we're going to destroy the parent anyway
31
+ counter_method = ('belongs_to_counter_cache_before_destroy_for_' + self.class.name.downcase).to_sym
32
+ if o.respond_to?(counter_method)
33
+ class << o
34
+ self
35
+ end.send(:define_method, counter_method, Proc.new {})
36
+ end
37
+ end
38
+
39
+ send(name).destroy_all
40
+ end
41
+ end
42
+
43
+ def define_restrict_dependency_method
44
+ name = self.name
45
+ mixin.redefine_method(dependency_method_name) do
46
+ raise DatastaxRails::DeleteRestrictionError.new(name) unless send(name).empty?
47
+ end
48
+ end
49
+
50
+ def dependency_method_name
51
+ "has_many_dependent_for_#{name}"
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,52 @@
1
+ module DatastaxRails::Associations::Builder
2
+ class HasOne < SingularAssociation #:nodoc:
3
+ self.macro = :has_one
4
+
5
+ def build
6
+ reflection = super
7
+ configure_dependency
8
+ reflection
9
+ end
10
+
11
+ private
12
+
13
+ def validate_options
14
+ valid_options = self.class.valid_options
15
+ valid_options += self.class.through_options if options[:through]
16
+ options.assert_valid_keys(valid_options)
17
+ end
18
+
19
+ def configure_dependency
20
+ if options[:dependent]
21
+ unless options[:dependent].in?([:destroy, :delete, :nullify, :restrict])
22
+ raise ArgumentError, "The :dependent option expects either :destroy, :delete, " \
23
+ ":nullify or :restrict (#{options[:dependent].inspect})"
24
+ end
25
+
26
+ send("define_#{options[:dependent]}_dependency_method")
27
+ model.before_destroy dependency_method_name
28
+ end
29
+ end
30
+
31
+ def dependency_method_name
32
+ "has_one_dependent_#{options[:dependent]}_for_#{name}"
33
+ end
34
+
35
+ def define_destroy_dependency_method
36
+ model.send(:class_eval, <<-eoruby, __FILE__, __LINE__ + 1)
37
+ def #{dependency_method_name}
38
+ association(#{name.to_sym.inspect}).delete
39
+ end
40
+ eoruby
41
+ end
42
+ alias :define_delete_dependency_method :define_destroy_dependency_method
43
+ alias :define_nullify_dependency_method :define_destroy_dependency_method
44
+
45
+ def define_restrict_dependency_method
46
+ name = self.name
47
+ model.redefine_method(dependency_method_name) do
48
+ raise DatastaxRails::DeleteRestrictionError.new(name) unless send(name).nil?
49
+ end
50
+ end
51
+ end
52
+ end