massive_record 0.2.1 → 0.2.2.rc1
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.
- data/CHANGELOG.md +58 -2
- data/Gemfile.lock +17 -17
- data/README.md +98 -41
- data/lib/massive_record.rb +2 -1
- data/lib/massive_record/adapters/thrift/hbase/hbase.rb +2425 -2154
- data/lib/massive_record/adapters/thrift/hbase/hbase_constants.rb +3 -3
- data/lib/massive_record/adapters/thrift/hbase/hbase_types.rb +195 -195
- data/lib/massive_record/adapters/thrift/row.rb +35 -4
- data/lib/massive_record/adapters/thrift/table.rb +49 -12
- data/lib/massive_record/orm/attribute_methods.rb +77 -5
- data/lib/massive_record/orm/attribute_methods/cast_numbers_on_write.rb +24 -0
- data/lib/massive_record/orm/attribute_methods/dirty.rb +18 -0
- data/lib/massive_record/orm/attribute_methods/time_zone_conversion.rb +24 -3
- data/lib/massive_record/orm/attribute_methods/write.rb +8 -1
- data/lib/massive_record/orm/base.rb +62 -8
- data/lib/massive_record/orm/column.rb +7 -11
- data/lib/massive_record/orm/default_id.rb +1 -1
- data/lib/massive_record/orm/embedded.rb +66 -0
- data/lib/massive_record/orm/errors.rb +17 -0
- data/lib/massive_record/orm/finders.rb +124 -71
- data/lib/massive_record/orm/finders/rescue_missing_table_on_find.rb +1 -1
- data/lib/massive_record/orm/finders/scope.rb +58 -34
- data/lib/massive_record/orm/id_factory.rb +22 -105
- data/lib/massive_record/orm/id_factory/atomic_incrementation.rb +117 -0
- data/lib/massive_record/orm/id_factory/timestamp.rb +60 -0
- data/lib/massive_record/orm/identity_map.rb +256 -0
- data/lib/massive_record/orm/log_subscriber.rb +18 -0
- data/lib/massive_record/orm/observer.rb +69 -0
- data/lib/massive_record/orm/persistence.rb +47 -119
- data/lib/massive_record/orm/persistence/operations.rb +100 -0
- data/lib/massive_record/orm/persistence/operations/atomic_operation.rb +71 -0
- data/lib/massive_record/orm/persistence/operations/destroy.rb +17 -0
- data/lib/massive_record/orm/persistence/operations/embedded/destroy.rb +26 -0
- data/lib/massive_record/orm/persistence/operations/embedded/insert.rb +27 -0
- data/lib/massive_record/orm/persistence/operations/embedded/operation_helpers.rb +66 -0
- data/lib/massive_record/orm/persistence/operations/embedded/reload.rb +39 -0
- data/lib/massive_record/orm/persistence/operations/embedded/update.rb +29 -0
- data/lib/massive_record/orm/persistence/operations/insert.rb +19 -0
- data/lib/massive_record/orm/persistence/operations/reload.rb +26 -0
- data/lib/massive_record/orm/persistence/operations/suppress.rb +15 -0
- data/lib/massive_record/orm/persistence/operations/table_operation_helpers.rb +106 -0
- data/lib/massive_record/orm/persistence/operations/update.rb +25 -0
- data/lib/massive_record/orm/query_instrumentation.rb +26 -49
- data/lib/massive_record/orm/raw_data.rb +47 -0
- data/lib/massive_record/orm/relations.rb +4 -0
- data/lib/massive_record/orm/relations/interface.rb +134 -0
- data/lib/massive_record/orm/relations/metadata.rb +58 -12
- data/lib/massive_record/orm/relations/proxy.rb +17 -12
- data/lib/massive_record/orm/relations/proxy/embedded_in.rb +54 -0
- data/lib/massive_record/orm/relations/proxy/embedded_in_polymorphic.rb +15 -0
- data/lib/massive_record/orm/relations/proxy/embeds_many.rb +215 -0
- data/lib/massive_record/orm/relations/proxy/references_many.rb +112 -88
- data/lib/massive_record/orm/relations/proxy/references_one.rb +1 -1
- data/lib/massive_record/orm/relations/proxy/references_one_polymorphic.rb +1 -1
- data/lib/massive_record/orm/relations/proxy_collection.rb +84 -0
- data/lib/massive_record/orm/schema/column_family.rb +3 -2
- data/lib/massive_record/orm/schema/{column_interface.rb → embedded_interface.rb} +38 -4
- data/lib/massive_record/orm/schema/field.rb +2 -0
- data/lib/massive_record/orm/schema/table_interface.rb +19 -2
- data/lib/massive_record/orm/single_table_inheritance.rb +37 -2
- data/lib/massive_record/orm/timestamps.rb +17 -7
- data/lib/massive_record/orm/validations.rb +4 -0
- data/lib/massive_record/orm/validations/associated.rb +50 -0
- data/lib/massive_record/rails/railtie.rb +31 -0
- data/lib/massive_record/version.rb +1 -1
- data/lib/massive_record/wrapper/cell.rb +8 -1
- data/massive_record.gemspec +4 -4
- data/spec/adapter/thrift/atomic_increment_spec.rb +16 -0
- data/spec/adapter/thrift/table_find_spec.rb +14 -2
- data/spec/adapter/thrift/table_spec.rb +6 -6
- data/spec/adapter/thrift/utf8_encoding_of_id_spec.rb +71 -0
- data/spec/orm/cases/attribute_methods_spec.rb +215 -22
- data/spec/orm/cases/auto_generate_id_spec.rb +1 -1
- data/spec/orm/cases/change_id_spec.rb +62 -0
- data/spec/orm/cases/default_id_spec.rb +25 -6
- data/spec/orm/cases/default_values_spec.rb +6 -3
- data/spec/orm/cases/dirty_spec.rb +150 -102
- data/spec/orm/cases/embedded_spec.rb +250 -0
- data/spec/orm/cases/{finder_default_scope.rb → finder_default_scope_spec.rb} +4 -0
- data/spec/orm/cases/finder_scope_spec.rb +96 -29
- data/spec/orm/cases/finders_spec.rb +57 -10
- data/spec/orm/cases/id_factory/atomic_incrementation_spec.rb +72 -0
- data/spec/orm/cases/id_factory/timestamp_spec.rb +61 -0
- data/spec/orm/cases/identity_map/identity_map_spec.rb +357 -0
- data/spec/orm/cases/identity_map/middleware_spec.rb +74 -0
- data/spec/orm/cases/log_subscriber_spec.rb +15 -2
- data/spec/orm/cases/observing_spec.rb +61 -0
- data/spec/orm/cases/persistence_spec.rb +151 -60
- data/spec/orm/cases/raw_data_spec.rb +58 -0
- data/spec/orm/cases/single_table_inheritance_spec.rb +58 -2
- data/spec/orm/cases/table_spec.rb +3 -3
- data/spec/orm/cases/time_zone_awareness_spec.rb +27 -0
- data/spec/orm/cases/timestamps_spec.rb +23 -109
- data/spec/orm/cases/validation_spec.rb +9 -0
- data/spec/orm/models/address.rb +5 -1
- data/spec/orm/models/address_with_timestamp.rb +12 -0
- data/spec/orm/models/car.rb +5 -0
- data/spec/orm/models/person.rb +13 -1
- data/spec/orm/models/person_with_timestamp.rb +4 -2
- data/spec/orm/models/test_class.rb +1 -0
- data/spec/orm/persistence/operations/atomic_operation_spec.rb +58 -0
- data/spec/orm/persistence/operations/destroy_spec.rb +22 -0
- data/spec/orm/persistence/operations/embedded/destroy_spec.rb +71 -0
- data/spec/orm/persistence/operations/embedded/insert_spec.rb +59 -0
- data/spec/orm/persistence/operations/embedded/operation_helpers_spec.rb +92 -0
- data/spec/orm/persistence/operations/embedded/reload_spec.rb +67 -0
- data/spec/orm/persistence/operations/embedded/update_spec.rb +60 -0
- data/spec/orm/persistence/operations/insert_spec.rb +31 -0
- data/spec/orm/persistence/operations/reload_spec.rb +48 -0
- data/spec/orm/persistence/operations/suppress_spec.rb +17 -0
- data/spec/orm/persistence/operations/table_operation_helpers_spec.rb +98 -0
- data/spec/orm/persistence/operations/update_spec.rb +25 -0
- data/spec/orm/persistence/operations_spec.rb +58 -0
- data/spec/orm/relations/interface_spec.rb +188 -0
- data/spec/orm/relations/metadata_spec.rb +92 -15
- data/spec/orm/relations/proxy/embedded_in_polymorphic_spec.rb +37 -0
- data/spec/orm/relations/proxy/embedded_in_spec.rb +66 -0
- data/spec/orm/relations/proxy/embeds_many_spec.rb +651 -0
- data/spec/orm/relations/proxy/references_many_spec.rb +466 -2
- data/spec/orm/schema/column_family_spec.rb +21 -0
- data/spec/orm/schema/embedded_interface_spec.rb +181 -0
- data/spec/orm/schema/field_spec.rb +7 -0
- data/spec/orm/schema/table_interface_spec.rb +31 -1
- data/spec/shared/orm/id_factories.rb +44 -0
- data/spec/shared/orm/model_with_timestamps.rb +132 -0
- data/spec/shared/orm/persistence/a_persistence_embedded_operation_class.rb +3 -0
- data/spec/shared/orm/persistence/a_persistence_operation_class.rb +11 -0
- data/spec/shared/orm/persistence/a_persistence_table_operation_class.rb +11 -0
- data/spec/shared/orm/relations/proxy.rb +9 -2
- data/spec/spec_helper.rb +9 -0
- data/spec/support/mock_massive_record_connection.rb +2 -1
- metadata +106 -21
- data/spec/orm/cases/column_spec.rb +0 -49
- data/spec/orm/cases/id_factory_spec.rb +0 -92
- data/spec/orm/schema/column_interface_spec.rb +0 -136
@@ -0,0 +1,84 @@
|
|
1
|
+
module MassiveRecord
|
2
|
+
module ORM
|
3
|
+
module Relations
|
4
|
+
|
5
|
+
#
|
6
|
+
# Proxy class for relations representing a collection
|
7
|
+
#
|
8
|
+
class ProxyCollection < Proxy
|
9
|
+
#
|
10
|
+
# Loading proxy_targets will merge it with records found currently in proxy,
|
11
|
+
# to make sure we don't remove any pushed proxy_targets only cause we load the
|
12
|
+
# proxy_targets.
|
13
|
+
#
|
14
|
+
def load_proxy_target(options = {})
|
15
|
+
proxy_target_before_load = proxy_target
|
16
|
+
proxy_target_after_load = super
|
17
|
+
|
18
|
+
self.proxy_target = (proxy_target_before_load + proxy_target_after_load).uniq
|
19
|
+
end
|
20
|
+
|
21
|
+
def reset(force = false)
|
22
|
+
super
|
23
|
+
@proxy_target = []
|
24
|
+
end
|
25
|
+
|
26
|
+
def replace(*records)
|
27
|
+
records.flatten!
|
28
|
+
|
29
|
+
if records.length == 1 and records.first.nil?
|
30
|
+
reset
|
31
|
+
else
|
32
|
+
delete_all
|
33
|
+
concat(records)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def first
|
38
|
+
limit(1).first
|
39
|
+
end
|
40
|
+
|
41
|
+
def empty?
|
42
|
+
length == 0
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Destroy record(s) from the collection
|
47
|
+
# Each record will be asked to destroy itself as well
|
48
|
+
#
|
49
|
+
def destroy(*records)
|
50
|
+
delete_or_destroy *records, :destroy
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
#
|
55
|
+
# Deletes record(s) from the collection
|
56
|
+
#
|
57
|
+
def delete(*records)
|
58
|
+
delete_or_destroy *records, :delete
|
59
|
+
end
|
60
|
+
|
61
|
+
#
|
62
|
+
# Destroys all records
|
63
|
+
#
|
64
|
+
def destroy_all
|
65
|
+
destroy(load_proxy_target).tap do
|
66
|
+
reset
|
67
|
+
loaded!
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
# Deletes all records from the relationship.
|
73
|
+
# Does not destroy the records
|
74
|
+
#
|
75
|
+
def delete_all
|
76
|
+
delete(load_proxy_target).tap do
|
77
|
+
reset
|
78
|
+
loaded!
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -5,7 +5,7 @@ module MassiveRecord
|
|
5
5
|
include ActiveModel::Validations
|
6
6
|
|
7
7
|
attr_accessor :column_families, :autoload_fields
|
8
|
-
attr_reader :name, :fields
|
8
|
+
attr_reader :name, :fields, :options_for_autoload_created_fields
|
9
9
|
|
10
10
|
|
11
11
|
validates_presence_of :name
|
@@ -91,8 +91,9 @@ module MassiveRecord
|
|
91
91
|
end
|
92
92
|
|
93
93
|
# Internal DSL method
|
94
|
-
def autoload_fields
|
94
|
+
def autoload_fields(field_args = {})
|
95
95
|
@autoload_fields = true
|
96
|
+
@options_for_autoload_created_fields = field_args
|
96
97
|
end
|
97
98
|
|
98
99
|
def autoload
|
@@ -3,7 +3,7 @@ require 'massive_record/orm/schema/common_interface'
|
|
3
3
|
module MassiveRecord
|
4
4
|
module ORM
|
5
5
|
module Schema
|
6
|
-
module
|
6
|
+
module EmbeddedInterface
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
9
9
|
included do
|
@@ -17,7 +17,7 @@ module MassiveRecord
|
|
17
17
|
#
|
18
18
|
# DSL method exposed into class. Makes it possible to do:
|
19
19
|
#
|
20
|
-
# class Person < MassiveRecord::ORM::
|
20
|
+
# class Person < MassiveRecord::ORM::Embedded
|
21
21
|
# field :name
|
22
22
|
# field :age, :integer, :default => 0
|
23
23
|
# field :points, :integer, :column => :number_of_points
|
@@ -52,6 +52,32 @@ module MassiveRecord
|
|
52
52
|
end
|
53
53
|
|
54
54
|
|
55
|
+
#
|
56
|
+
# Returns attributes in embedded object from raw data. Raw
|
57
|
+
# data's keys might different from a field name, if :column
|
58
|
+
# option has been used.
|
59
|
+
#
|
60
|
+
def transpose_raw_data_to_record_attributes_and_raw_data(id, raw_data)
|
61
|
+
attributes = {:id => id}
|
62
|
+
attributes['updated_at'] = raw_data.created_at
|
63
|
+
|
64
|
+
raw_attributes = if raw_data.value.is_a? String
|
65
|
+
Base.coder.load(raw_data.value)
|
66
|
+
else
|
67
|
+
raw_data.value
|
68
|
+
end
|
69
|
+
|
70
|
+
raw_data = Hash[raw_attributes.collect do |attr, value|
|
71
|
+
[attr, RawData.new(value: value, created_at: raw_data.created_at)]
|
72
|
+
end]
|
73
|
+
|
74
|
+
attributes_schema.each do |attr_name, orm_field|
|
75
|
+
value = raw_attributes.has_key?(orm_field.column) ? raw_attributes[orm_field.column] : nil
|
76
|
+
attributes[attr_name] = value.nil? ? nil : orm_field.decode(raw_attributes[orm_field.column])
|
77
|
+
end
|
78
|
+
|
79
|
+
[attributes, raw_data]
|
80
|
+
end
|
55
81
|
|
56
82
|
|
57
83
|
private
|
@@ -77,10 +103,13 @@ module MassiveRecord
|
|
77
103
|
send(method, new_field.default) if respond_to? method
|
78
104
|
end
|
79
105
|
|
106
|
+
|
80
107
|
#
|
81
|
-
#
|
108
|
+
# Returns attributes as a hash which has correct keys
|
109
|
+
# based on it's field definition. For instance, you can
|
110
|
+
# have a class with a field :attr_name, :column => :stored_as_this
|
82
111
|
#
|
83
|
-
def
|
112
|
+
def attributes_db_raw_data_hash(only_attr_names = [])
|
84
113
|
values = Hash.new
|
85
114
|
|
86
115
|
attributes_schema.each do |attr_name, orm_field|
|
@@ -90,6 +119,11 @@ module MassiveRecord
|
|
90
119
|
|
91
120
|
values
|
92
121
|
end
|
122
|
+
|
123
|
+
def attributes_to_row_values_hash(only_attr_names = [])
|
124
|
+
ActiveSupport::Deprecation.warn("attributes_to_row_values_hash is deprecated. Please use attributes_db_raw_data_hash")
|
125
|
+
attributes_db_raw_data_hash(only_attr_names)
|
126
|
+
end
|
93
127
|
end
|
94
128
|
end
|
95
129
|
end
|
@@ -28,8 +28,15 @@ module MassiveRecord
|
|
28
28
|
#
|
29
29
|
#
|
30
30
|
def column_family(name, &block)
|
31
|
+
add_column_family(name).instance_eval(&block)
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# Adds a column family to your class
|
36
|
+
#
|
37
|
+
def add_column_family(name)
|
31
38
|
ensure_column_families_exists
|
32
|
-
column_families.family_by_name_or_new(name)
|
39
|
+
column_families.family_by_name_or_new(name)
|
33
40
|
end
|
34
41
|
|
35
42
|
#
|
@@ -54,15 +61,25 @@ module MassiveRecord
|
|
54
61
|
# It should be on a unique and complete form like ["info:name", "info:phone"]
|
55
62
|
#
|
56
63
|
def autoload_column_families_and_fields_with(column_names)
|
64
|
+
ensure_column_families_exists
|
65
|
+
|
57
66
|
column_names.each do |column_family_and_column_name|
|
58
67
|
family_name, column_name = column_family_and_column_name.split(":")
|
59
68
|
|
60
69
|
if family = column_families.family_by_name(family_name) and family.autoload_fields?
|
61
|
-
family.add?
|
70
|
+
family.add?(Field.new(
|
71
|
+
family.options_for_autoload_created_fields.merge(:name => column_name)
|
72
|
+
))
|
62
73
|
end
|
63
74
|
end
|
64
75
|
end
|
65
76
|
|
77
|
+
#
|
78
|
+
# Makes it a bit more convenient to get all the column family names
|
79
|
+
#
|
80
|
+
def known_column_family_names
|
81
|
+
(column_families || []).collect &:name
|
82
|
+
end
|
66
83
|
|
67
84
|
|
68
85
|
|
@@ -3,23 +3,54 @@ module MassiveRecord
|
|
3
3
|
module SingleTableInheritance
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
|
+
#
|
7
|
+
# Raised if you call first on any sub class of your base class.
|
8
|
+
# Calling first() on a sub class can't be easily done through
|
9
|
+
# thrift as we can't apply any column filter on the STI type column.
|
10
|
+
#
|
11
|
+
# What you need to do is, the very inefficient, SubClass.all.first
|
12
|
+
# When doing an all, all records will be fetched, but at least the
|
13
|
+
# array or records will be filtered and only correct class will be
|
14
|
+
# returned.
|
15
|
+
#
|
16
|
+
class FirstUnsupported < MassiveRecordError; end
|
17
|
+
|
18
|
+
|
6
19
|
included do
|
7
20
|
after_initialize :ensure_proper_type
|
8
21
|
end
|
9
22
|
|
10
23
|
|
11
24
|
module ClassMethods
|
25
|
+
def do_find(*args)
|
26
|
+
result = super
|
27
|
+
single_table_inheritance_enabled? ? ensure_only_class_or_subclass_of_self_are_returned(result) : result
|
28
|
+
end
|
29
|
+
|
30
|
+
def first(*args)
|
31
|
+
if base_class == self
|
32
|
+
super
|
33
|
+
else
|
34
|
+
raise FirstUnsupported.new("Sorry, first() on '#{self}' (sub class of the base class '#{base_class}') is unsupported due to unable to efficiently filter this through Thrift.")
|
35
|
+
end
|
36
|
+
end
|
12
37
|
|
13
38
|
private
|
14
39
|
|
40
|
+
def ensure_only_class_or_subclass_of_self_are_returned(result)
|
41
|
+
multiple_result = result.is_a? Array
|
42
|
+
filtered_results = (multiple_result ? result : [result]).select { |result| result.kind_of? self }
|
43
|
+
multiple_result ? filtered_results : filtered_results.first
|
44
|
+
end
|
45
|
+
|
15
46
|
#
|
16
47
|
# In Rails development environment class files are not required before they are needed.
|
17
48
|
#
|
18
|
-
#
|
49
|
+
# transpose_hbase_row_to_record_attributes_and_raw_data uses attributes_schema and
|
19
50
|
# for attributes_schema to have loaded all of it's fields correctly we need
|
20
51
|
# to make sure Rails loads class file before attributes_schema renders it's schema.
|
21
52
|
#
|
22
|
-
def
|
53
|
+
def transpose_hbase_row_to_record_attributes_and_raw_data(row) # :nodoc:
|
23
54
|
if field = attributes_schema[inheritance_attribute]
|
24
55
|
if cell_with_record_sti_class = row.columns[field.unique_name] and cell_with_record_sti_class.present?
|
25
56
|
if klass = field.decode(cell_with_record_sti_class.value) and klass.present?
|
@@ -34,6 +65,10 @@ module MassiveRecord
|
|
34
65
|
def ensure_sti_class_is_loaded(klass) # :nodoc:
|
35
66
|
klass.constantize
|
36
67
|
end
|
68
|
+
|
69
|
+
def single_table_inheritance_enabled?
|
70
|
+
!!attributes_schema[inheritance_attribute]
|
71
|
+
end
|
37
72
|
end
|
38
73
|
|
39
74
|
|
@@ -9,6 +9,10 @@ module MassiveRecord
|
|
9
9
|
raise "created_at must be of type time" if attributes_schema['created_at'].type != :time
|
10
10
|
@attributes['created_at'] = Time.now
|
11
11
|
end
|
12
|
+
|
13
|
+
before_create do
|
14
|
+
@attributes['updated_at'] = @attributes['created_at'] || Time.now
|
15
|
+
end
|
12
16
|
end
|
13
17
|
|
14
18
|
|
@@ -17,10 +21,11 @@ module MassiveRecord
|
|
17
21
|
module ClassMethods
|
18
22
|
private
|
19
23
|
|
20
|
-
def
|
21
|
-
attributes
|
22
|
-
|
23
|
-
|
24
|
+
def transpose_hbase_row_to_record_attributes_and_raw_data(row)
|
25
|
+
super.tap do |attributes, raw_values|
|
26
|
+
attributes['updated_at'] = row.updated_at
|
27
|
+
attributes
|
28
|
+
end
|
24
29
|
end
|
25
30
|
end
|
26
31
|
|
@@ -31,6 +36,10 @@ module MassiveRecord
|
|
31
36
|
self.class.time_zone_aware_attributes ? self['updated_at'].try(:in_time_zone) : self['updated_at']
|
32
37
|
end
|
33
38
|
|
39
|
+
def updated_at=(time)
|
40
|
+
write_attribute :updated_at, time
|
41
|
+
end
|
42
|
+
|
34
43
|
def write_attribute(attr_name, value)
|
35
44
|
attr_name = attr_name.to_s
|
36
45
|
|
@@ -46,9 +55,10 @@ module MassiveRecord
|
|
46
55
|
private
|
47
56
|
|
48
57
|
def update(*)
|
49
|
-
#
|
50
|
-
# the
|
51
|
-
#
|
58
|
+
# Sets updated at to Time.now, even though the updated at is
|
59
|
+
# read from the cell's time stamp on relad. We do this after
|
60
|
+
# a successfully update to remove the need to do a query to
|
61
|
+
# the db again to get the updated at timestamp.
|
52
62
|
@attributes['updated_at'] = Time.now if updated = super
|
53
63
|
updated
|
54
64
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module MassiveRecord
|
2
|
+
module ORM
|
3
|
+
module Validations
|
4
|
+
class AssociatedValidator < ActiveModel::EachValidator
|
5
|
+
def validate_each(record, attribute, value)
|
6
|
+
return if (value.is_a?(Array) ? value : [value]).collect{ |r| r.blank? || r.valid? }.all?
|
7
|
+
record.errors.add(attribute, :invalid, options.merge(:value => value))
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
# Validates whether the associated object or objects are all valid themselves. Works with any kind of association.
|
13
|
+
#
|
14
|
+
# class Book < MassiveRecord::ORM::Table
|
15
|
+
# has_many :pages
|
16
|
+
# belongs_to :library
|
17
|
+
#
|
18
|
+
# validates_associated :pages, :library
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# Warning: If, after the above definition, you then wrote:
|
22
|
+
#
|
23
|
+
# class Page < MassiveRecord::ORM::Table
|
24
|
+
# belongs_to :book
|
25
|
+
#
|
26
|
+
# validates_associated :book
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# this would specify a circular dependency and cause infinite recursion.
|
30
|
+
#
|
31
|
+
# NOTE: This validation will not fail if the association hasn't been assigned. If you want to
|
32
|
+
# ensure that the association is both present and guaranteed to be valid, you also need to
|
33
|
+
# use +validates_presence_of+.
|
34
|
+
#
|
35
|
+
# Configuration options:
|
36
|
+
# * <tt>:message</tt> - A custom error message (default is: "is invalid")
|
37
|
+
# * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
|
38
|
+
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
|
39
|
+
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
|
40
|
+
# method, proc or string should return or evaluate to a true or false value.
|
41
|
+
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
|
42
|
+
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
43
|
+
# method, proc or string should return or evaluate to a true or false value.
|
44
|
+
def validates_associated(*attr_names)
|
45
|
+
validates_with AssociatedValidator, _merge_attributes(attr_names)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module MassiveRecord
|
2
2
|
module Rails
|
3
3
|
class Railtie < ::Rails::Railtie
|
4
|
+
config.massive_record = ActiveSupport::OrderedOptions.new
|
5
|
+
|
4
6
|
initializer "massive_record.logger" do
|
5
7
|
MassiveRecord::ORM::Base.logger = ::Rails.logger
|
6
8
|
end
|
@@ -20,12 +22,41 @@ module MassiveRecord
|
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
25
|
+
# Insert IdentityMap's middleware if enabled
|
26
|
+
initializer "massive_record.identity_map" do |app|
|
27
|
+
if config.massive_record.delete(:identity_map)
|
28
|
+
config.app_middleware.insert_after "::ActionDispatch::Callbacks", "MassiveRecord::ORM::IdentityMap::Middleware"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
23
32
|
initializer "massive_record.time_zone_awareness" do
|
24
33
|
ActiveSupport.on_load(:massive_record) do
|
25
34
|
self.time_zone_aware_attributes = true
|
26
35
|
self.default_timezone = :utc
|
27
36
|
end
|
28
37
|
end
|
38
|
+
|
39
|
+
initializer "massive_record.set_configs" do |app|
|
40
|
+
ActiveSupport.on_load(:massive_record) do
|
41
|
+
app.config.massive_record.each { |k,v| send("#{k}=", v) }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
config.after_initialize do
|
46
|
+
ActiveSupport.on_load(:massive_record) do
|
47
|
+
instantiate_observers
|
48
|
+
|
49
|
+
if ::Rails::VERSION::MAJOR >= 3 && ::Rails::VERSION::MINOR >= 1
|
50
|
+
ActionDispatch::Reloader.to_prepare do
|
51
|
+
MassiveRecord::ORM::Base.instantiate_observers
|
52
|
+
end
|
53
|
+
else
|
54
|
+
ActionDispatch::Callbacks.to_prepare(:massive_record_instantiate_observers) do
|
55
|
+
MassiveRecord::ORM::Base.instantiate_observers
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
29
60
|
end
|
30
61
|
end
|
31
62
|
end
|