bigrecord 0.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 (104) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +44 -0
  3. data/Rakefile +17 -0
  4. data/VERSION +1 -0
  5. data/doc/bigrecord_specs.rdoc +36 -0
  6. data/doc/getting_started.rdoc +157 -0
  7. data/examples/bigrecord.yml +25 -0
  8. data/generators/bigrecord/bigrecord_generator.rb +17 -0
  9. data/generators/bigrecord/templates/bigrecord.rake +47 -0
  10. data/generators/bigrecord_migration/bigrecord_migration_generator.rb +13 -0
  11. data/generators/bigrecord_migration/templates/migration.rb +9 -0
  12. data/generators/bigrecord_model/bigrecord_model_generator.rb +28 -0
  13. data/generators/bigrecord_model/templates/migration.rb +13 -0
  14. data/generators/bigrecord_model/templates/model.rb +7 -0
  15. data/generators/bigrecord_model/templates/model_spec.rb +12 -0
  16. data/init.rb +9 -0
  17. data/install.rb +22 -0
  18. data/lib/big_record/abstract_base.rb +1088 -0
  19. data/lib/big_record/action_view_extensions.rb +266 -0
  20. data/lib/big_record/ar_associations/association_collection.rb +194 -0
  21. data/lib/big_record/ar_associations/association_proxy.rb +158 -0
  22. data/lib/big_record/ar_associations/belongs_to_association.rb +57 -0
  23. data/lib/big_record/ar_associations/belongs_to_many_association.rb +57 -0
  24. data/lib/big_record/ar_associations/has_and_belongs_to_many_association.rb +164 -0
  25. data/lib/big_record/ar_associations/has_many_association.rb +191 -0
  26. data/lib/big_record/ar_associations/has_one_association.rb +80 -0
  27. data/lib/big_record/ar_associations.rb +1608 -0
  28. data/lib/big_record/ar_reflection.rb +223 -0
  29. data/lib/big_record/attribute_methods.rb +75 -0
  30. data/lib/big_record/base.rb +618 -0
  31. data/lib/big_record/br_associations/association_collection.rb +194 -0
  32. data/lib/big_record/br_associations/association_proxy.rb +153 -0
  33. data/lib/big_record/br_associations/belongs_to_association.rb +52 -0
  34. data/lib/big_record/br_associations/belongs_to_many_association.rb +293 -0
  35. data/lib/big_record/br_associations/cached_item_proxy.rb +194 -0
  36. data/lib/big_record/br_associations/cached_item_proxy_factory.rb +62 -0
  37. data/lib/big_record/br_associations/has_and_belongs_to_many_association.rb +168 -0
  38. data/lib/big_record/br_associations/has_one_association.rb +80 -0
  39. data/lib/big_record/br_associations.rb +978 -0
  40. data/lib/big_record/br_reflection.rb +151 -0
  41. data/lib/big_record/callbacks.rb +367 -0
  42. data/lib/big_record/connection_adapters/abstract/connection_specification.rb +279 -0
  43. data/lib/big_record/connection_adapters/abstract/database_statements.rb +175 -0
  44. data/lib/big_record/connection_adapters/abstract/quoting.rb +58 -0
  45. data/lib/big_record/connection_adapters/abstract_adapter.rb +190 -0
  46. data/lib/big_record/connection_adapters/column.rb +491 -0
  47. data/lib/big_record/connection_adapters/hbase_adapter.rb +432 -0
  48. data/lib/big_record/connection_adapters/view.rb +27 -0
  49. data/lib/big_record/connection_adapters.rb +10 -0
  50. data/lib/big_record/deletion.rb +73 -0
  51. data/lib/big_record/dynamic_schema.rb +92 -0
  52. data/lib/big_record/embedded.rb +71 -0
  53. data/lib/big_record/embedded_associations/association_proxy.rb +148 -0
  54. data/lib/big_record/family_span_columns.rb +89 -0
  55. data/lib/big_record/fixtures.rb +1025 -0
  56. data/lib/big_record/migration.rb +380 -0
  57. data/lib/big_record/routing_ext.rb +65 -0
  58. data/lib/big_record/timestamp.rb +51 -0
  59. data/lib/big_record/validations.rb +830 -0
  60. data/lib/big_record.rb +125 -0
  61. data/lib/bigrecord.rb +1 -0
  62. data/rails/init.rb +9 -0
  63. data/spec/connections/bigrecord.yml +13 -0
  64. data/spec/connections/cassandra/connection.rb +2 -0
  65. data/spec/connections/hbase/connection.rb +2 -0
  66. data/spec/debug.log +281 -0
  67. data/spec/integration/br_associations_spec.rb +80 -0
  68. data/spec/lib/animal.rb +12 -0
  69. data/spec/lib/book.rb +10 -0
  70. data/spec/lib/broken_migrations/duplicate_name/20090706182535_add_animals_table.rb +14 -0
  71. data/spec/lib/broken_migrations/duplicate_name/20090706193019_add_animals_table.rb +9 -0
  72. data/spec/lib/broken_migrations/duplicate_version/20090706190623_add_books_table.rb +9 -0
  73. data/spec/lib/broken_migrations/duplicate_version/20090706190623_add_companies_table.rb +9 -0
  74. data/spec/lib/company.rb +14 -0
  75. data/spec/lib/embedded/web_link.rb +12 -0
  76. data/spec/lib/employee.rb +33 -0
  77. data/spec/lib/migrations/20090706182535_add_animals_table.rb +13 -0
  78. data/spec/lib/migrations/20090706190623_add_books_table.rb +15 -0
  79. data/spec/lib/migrations/20090706193019_add_companies_table.rb +14 -0
  80. data/spec/lib/migrations/20090706194512_add_employees_table.rb +13 -0
  81. data/spec/lib/migrations/20090706195741_add_zoos_table.rb +13 -0
  82. data/spec/lib/novel.rb +5 -0
  83. data/spec/lib/zoo.rb +17 -0
  84. data/spec/spec.opts +4 -0
  85. data/spec/spec_helper.rb +55 -0
  86. data/spec/unit/abstract_base_spec.rb +287 -0
  87. data/spec/unit/adapters/abstract_adapter_spec.rb +56 -0
  88. data/spec/unit/adapters/adapter_shared_spec.rb +51 -0
  89. data/spec/unit/adapters/hbase_adapter_spec.rb +15 -0
  90. data/spec/unit/ar_associations_spec.rb +8 -0
  91. data/spec/unit/base_spec.rb +6 -0
  92. data/spec/unit/br_associations_spec.rb +58 -0
  93. data/spec/unit/embedded_spec.rb +43 -0
  94. data/spec/unit/find_spec.rb +34 -0
  95. data/spec/unit/hash_helper_spec.rb +44 -0
  96. data/spec/unit/migration_spec.rb +144 -0
  97. data/spec/unit/model_spec.rb +315 -0
  98. data/spec/unit/validations_spec.rb +182 -0
  99. data/tasks/bigrecord_tasks.rake +47 -0
  100. data/tasks/data_store.rb +46 -0
  101. data/tasks/gem.rb +22 -0
  102. data/tasks/rdoc.rb +8 -0
  103. data/tasks/spec.rb +34 -0
  104. metadata +189 -0
@@ -0,0 +1,223 @@
1
+ module BigRecord
2
+ module ArReflection # :nodoc:
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ # Reflection allows you to interrogate Active Record classes and objects about their associations and aggregations.
8
+ # This information can, for example, be used in a form builder that took an Active Record object and created input
9
+ # fields for all of the attributes depending on their type and displayed the associations to other objects.
10
+ #
11
+ # You can find the interface for the AggregateReflection and AssociationReflection classes in the abstract MacroReflection class.
12
+ module ClassMethods
13
+ def create_reflection(macro, name, options, big_record)
14
+ case macro
15
+ when :has_many, :belongs_to, :has_one, :has_and_belongs_to_many
16
+ reflection = ArAssociationReflection.new(macro, name, options, big_record)
17
+ when :composed_of
18
+ reflection = ArAggregateReflection.new(macro, name, options, big_record)
19
+ end
20
+ write_inheritable_hash :reflections, name => reflection
21
+ reflection
22
+ end
23
+
24
+ # Returns a hash containing all AssociationReflection objects for the current class
25
+ # Example:
26
+ #
27
+ # Invoice.reflections
28
+ # Account.reflections
29
+ #
30
+ def reflections
31
+ read_inheritable_attribute(:reflections) || write_inheritable_attribute(:reflections, {})
32
+ end
33
+
34
+ # Returns an array of AggregateReflection objects for all the aggregations in the class.
35
+ def reflect_on_all_aggregations
36
+ reflections.values.select { |reflection| reflection.is_a?(AggregateReflection) }
37
+ end
38
+
39
+ # Returns the AggregateReflection object for the named +aggregation+ (use the symbol). Example:
40
+ #
41
+ # Account.reflect_on_aggregation(:balance) # returns the balance AggregateReflection
42
+ #
43
+ def reflect_on_aggregation(aggregation)
44
+ reflections[aggregation].is_a?(AggregateReflection) ? reflections[aggregation] : nil
45
+ end
46
+
47
+ # Returns an array of AssociationReflection objects for all the aggregations in the class. If you only want to reflect on a
48
+ # certain association type, pass in the symbol (:has_many, :has_one, :belongs_to) for that as the first parameter.
49
+ # Example:
50
+ #
51
+ # Account.reflect_on_all_associations # returns an array of all associations
52
+ # Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
53
+ #
54
+ def reflect_on_all_associations(macro = nil)
55
+ association_reflections = reflections.values.select { |reflection| reflection.is_a?(AssociationReflection) }
56
+ macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
57
+ end
58
+
59
+ # Returns the AssociationReflection object for the named +aggregation+ (use the symbol). Example:
60
+ #
61
+ # Account.reflect_on_association(:owner) # returns the owner AssociationReflection
62
+ # Invoice.reflect_on_association(:line_items).macro # returns :has_many
63
+ #
64
+ def reflect_on_association(association)
65
+ reflections[association].is_a?(AssociationReflection) ? reflections[association] : nil
66
+ end
67
+ end
68
+
69
+
70
+ # Abstract base class for AggregateReflection and AssociationReflection that describes the interface available for both of
71
+ # those classes. Objects of AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
72
+ class MacroReflection
73
+ attr_reader :big_record
74
+ def initialize(macro, name, options, big_record)
75
+ @macro, @name, @options, @big_record = macro, name, options, big_record
76
+ end
77
+
78
+ # Returns the name of the macro, so it would return :balance for "composed_of :balance, :class_name => 'Money'" or
79
+ # :clients for "has_many :clients".
80
+ def name
81
+ @name
82
+ end
83
+
84
+ # Returns the name of the macro, so it would return :composed_of for
85
+ # "composed_of :balance, :class_name => 'Money'" or :has_many for "has_many :clients".
86
+ def macro
87
+ @macro
88
+ end
89
+
90
+ # Returns the hash of options used for the macro, so it would return { :class_name => "Money" } for
91
+ # "composed_of :balance, :class_name => 'Money'" or {} for "has_many :clients".
92
+ def options
93
+ @options
94
+ end
95
+
96
+ # Returns the class for the macro, so "composed_of :balance, :class_name => 'Money'" would return the Money class and
97
+ # "has_many :clients" would return the Client class.
98
+ def klass() end
99
+
100
+ def class_name
101
+ @class_name ||= name_to_class_name(name.id2name)
102
+ end
103
+
104
+ def ==(other_aggregation)
105
+ name == other_aggregation.name && other_aggregation.options && big_record == other_aggregation.big_record
106
+ end
107
+ end
108
+
109
+
110
+ # Holds all the meta-data about an aggregation as it was specified in the Active Record class.
111
+ class ArAggregateReflection < MacroReflection #:nodoc:
112
+ def klass
113
+ @klass ||= Object.const_get(options[:class_name] || class_name)
114
+ end
115
+
116
+ private
117
+ def name_to_class_name(name)
118
+ name.capitalize.gsub(/_(.)/) { |s| $1.capitalize }
119
+ end
120
+ end
121
+
122
+ # Holds all the meta-data about an association as it was specified in the Active Record class.
123
+ class ArAssociationReflection < MacroReflection #:nodoc:
124
+ def klass
125
+ @klass ||= big_record.send(:compute_type, class_name)
126
+ end
127
+
128
+ def table_name
129
+ @table_name ||= klass.table_name
130
+ end
131
+
132
+ def primary_key_name
133
+ return @primary_key_name if @primary_key_name
134
+ case
135
+ when macro == :belongs_to
136
+ @primary_key_name = options[:foreign_key] || class_name.foreign_key
137
+ when options[:as]
138
+ @primary_key_name = options[:foreign_key] || "#{options[:as]}_id"
139
+ else
140
+ @primary_key_name = options[:foreign_key] || big_record.name.foreign_key
141
+ end
142
+ end
143
+
144
+ def association_foreign_key
145
+ @association_foreign_key ||= @options[:association_foreign_key] || class_name.foreign_key
146
+ end
147
+
148
+ def counter_cache_column
149
+ if options[:counter_cache] == true
150
+ "#{big_record.name.underscore.pluralize}_count"
151
+ elsif options[:counter_cache]
152
+ options[:counter_cache]
153
+ end
154
+ end
155
+
156
+ def through_reflection
157
+ @through_reflection ||= options[:through] ? big_record.reflect_on_association(options[:through]) : false
158
+ end
159
+
160
+ # Gets an array of possible :through source reflection names
161
+ #
162
+ # [singularized, pluralized]
163
+ #
164
+ def source_reflection_names
165
+ @source_reflection_names ||= (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }
166
+ end
167
+
168
+ # Gets the source of the through reflection. It checks both a singularized and pluralized form for :belongs_to or :has_many.
169
+ # (The :tags association on Tagging below)
170
+ #
171
+ # class Post
172
+ # has_many :tags, :through => :taggings
173
+ # end
174
+ #
175
+ def source_reflection
176
+ return nil unless through_reflection
177
+ @source_reflection ||= source_reflection_names.collect { |name| through_reflection.klass.reflect_on_association(name) }.compact.first
178
+ end
179
+
180
+ def check_validity!
181
+ if options[:through]
182
+ if through_reflection.nil?
183
+ raise HasManyThroughAssociationNotFoundError.new(big_record.name, self)
184
+ end
185
+
186
+ if source_reflection.nil?
187
+ raise HasManyThroughSourceAssociationNotFoundError.new(self)
188
+ end
189
+
190
+ if options[:source_type] && source_reflection.options[:polymorphic].nil?
191
+ raise HasManyThroughAssociationPointlessSourceTypeError.new(big_record.name, self, source_reflection)
192
+ end
193
+
194
+ if source_reflection.options[:polymorphic] && options[:source_type].nil?
195
+ raise HasManyThroughAssociationPolymorphicError.new(big_record.name, self, source_reflection)
196
+ end
197
+
198
+ unless [:belongs_to, :has_many].include?(source_reflection.macro) && source_reflection.options[:through].nil?
199
+ raise HasManyThroughSourceAssociationMacroError.new(self)
200
+ end
201
+ end
202
+ end
203
+
204
+ private
205
+ def name_to_class_name(name)
206
+ if name =~ /::/
207
+ name
208
+ else
209
+ if options[:class_name]
210
+ options[:class_name]
211
+ elsif through_reflection # get the class_name of the belongs_to association of the through reflection
212
+ options[:source_type] || source_reflection.class_name
213
+ else
214
+ class_name = name.to_s.camelize
215
+ class_name = class_name.singularize if [ :has_many, :has_and_belongs_to_many ].include?(macro)
216
+ class_name
217
+ end
218
+ end
219
+ end
220
+ end
221
+
222
+ end
223
+ end
@@ -0,0 +1,75 @@
1
+ module BigRecord
2
+ module AttributeMethods #:nodoc:
3
+ DEFAULT_SUFFIXES = %w(= ? _before_type_cast)
4
+
5
+ def self.included(base)
6
+ base.extend ClassMethods
7
+ base.attribute_method_suffix(*DEFAULT_SUFFIXES)
8
+ end
9
+
10
+ # Declare and check for suffixed attribute methods.
11
+ module ClassMethods
12
+ # Declare a method available for all attributes with the given suffix.
13
+ # Uses method_missing and respond_to? to rewrite the method
14
+ # #{attr}#{suffix}(*args, &block)
15
+ # to
16
+ # attribute#{suffix}(#{attr}, *args, &block)
17
+ #
18
+ # An attribute#{suffix} instance method must exist and accept at least
19
+ # the attr argument.
20
+ #
21
+ # For example:
22
+ # class Person < ActiveRecord::Base
23
+ # attribute_method_suffix '_changed?'
24
+ #
25
+ # private
26
+ # def attribute_changed?(attr)
27
+ # ...
28
+ # end
29
+ # end
30
+ #
31
+ # person = Person.find(1)
32
+ # person.name_changed? # => false
33
+ # person.name = 'Hubert'
34
+ # person.name_changed? # => true
35
+ def attribute_method_suffix(*suffixes)
36
+ attribute_method_suffixes.concat suffixes
37
+ rebuild_attribute_method_regexp
38
+ end
39
+
40
+ # Returns MatchData if method_name is an attribute method.
41
+ def match_attribute_method?(method_name)
42
+ rebuild_attribute_method_regexp unless defined?(@@attribute_method_regexp) && @@attribute_method_regexp
43
+ @@attribute_method_regexp.match(method_name)
44
+ end
45
+
46
+ private
47
+ # Suffixes a, ?, c become regexp /(a|\?|c)$/
48
+ def rebuild_attribute_method_regexp
49
+ suffixes = attribute_method_suffixes.map { |s| Regexp.escape(s) }
50
+ @@attribute_method_regexp = /(#{suffixes.join('|')})$/.freeze
51
+ end
52
+
53
+ # Default to =, ?, _before_type_cast
54
+ def attribute_method_suffixes
55
+ @@attribute_method_suffixes ||= []
56
+ end
57
+ end
58
+
59
+ private
60
+ # Handle *? for method_missing.
61
+ def attribute?(attribute_name)
62
+ query_attribute(attribute_name)
63
+ end
64
+
65
+ # Handle *= for method_missing.
66
+ def attribute=(attribute_name, value)
67
+ write_attribute(attribute_name, value)
68
+ end
69
+
70
+ # Handle *_before_type_cast for method_missing.
71
+ def attribute_before_type_cast(attribute_name)
72
+ read_attribute_before_type_cast(attribute_name)
73
+ end
74
+ end
75
+ end