datastax_rails 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
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