axle_attributes 1.13.2
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.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.travis.yml +16 -0
- data/Gemfile +15 -0
- data/README.md +10 -0
- data/Rakefile +18 -0
- data/app/models/attribute_customization.rb +7 -0
- data/app/models/attribute_customization/local_cache.rb +28 -0
- data/app/models/attribute_mapping.rb +15 -0
- data/app/models/attribute_mapping/local_cache.rb +40 -0
- data/axle_attributes.gemspec +22 -0
- data/bin/rails +8 -0
- data/db/migrate/20131118042651_create_attribute_customizations.rb +16 -0
- data/db/migrate/20131121013237_create_attribute_mappings.rb +17 -0
- data/db/migrate/20151120160522_add_editable_to_attribute_customizations.rb +7 -0
- data/lib/axle_attributes.rb +30 -0
- data/lib/axle_attributes/child_definition.rb +42 -0
- data/lib/axle_attributes/conversions.rb +36 -0
- data/lib/axle_attributes/definition.rb +118 -0
- data/lib/axle_attributes/definition/callbacks.rb +34 -0
- data/lib/axle_attributes/definition/customization.rb +51 -0
- data/lib/axle_attributes/definition/formatted.rb +57 -0
- data/lib/axle_attributes/definition/indexed.rb +41 -0
- data/lib/axle_attributes/definition/mappings.rb +48 -0
- data/lib/axle_attributes/dumper.rb +54 -0
- data/lib/axle_attributes/engine.rb +4 -0
- data/lib/axle_attributes/format.rb +74 -0
- data/lib/axle_attributes/has_attributes.rb +104 -0
- data/lib/axle_attributes/has_attributes/json_reader.rb +87 -0
- data/lib/axle_attributes/model.rb +34 -0
- data/lib/axle_attributes/null_definition.rb +11 -0
- data/lib/axle_attributes/parent_definition.rb +37 -0
- data/lib/axle_attributes/provided.rb +67 -0
- data/lib/axle_attributes/regex.rb +4 -0
- data/lib/axle_attributes/segmented.rb +30 -0
- data/lib/axle_attributes/serializations.rb +147 -0
- data/lib/axle_attributes/serializations/builder/serialization.rb +83 -0
- data/lib/axle_attributes/serializations/builder/serialize_many.rb +17 -0
- data/lib/axle_attributes/serializations/builder/serialize_one.rb +7 -0
- data/lib/axle_attributes/serializations/reflection.rb +40 -0
- data/lib/axle_attributes/serializations/serialization.rb +88 -0
- data/lib/axle_attributes/serializations/serialize_many.rb +88 -0
- data/lib/axle_attributes/serializations/serialize_one.rb +34 -0
- data/lib/axle_attributes/serialized_child.rb +58 -0
- data/lib/axle_attributes/validations.rb +29 -0
- data/lib/axle_attributes/versioned.rb +21 -0
- data/test/dummy/README.rdoc +6 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/images/.keep +0 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/controllers/concerns/.keep +0 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/mailers/.keep +0 -0
- data/test/dummy/app/models/.keep +0 -0
- data/test/dummy/app/models/concerns/.keep +0 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +12 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +13 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +29 -0
- data/test/dummy/config/environments/production.rb +80 -0
- data/test/dummy/config/environments/test.rb +38 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +12 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +56 -0
- data/test/dummy/db/schema.rb +50 -0
- data/test/dummy/lib/assets/.keep +0 -0
- data/test/dummy/log/.keep +0 -0
- data/test/dummy/public/404.html +58 -0
- data/test/dummy/public/422.html +58 -0
- data/test/dummy/public/500.html +57 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/factories/all.rb +14 -0
- data/test/helper.rb +25 -0
- data/test/lib/child_definition_test.rb +43 -0
- data/test/lib/conversions_test.rb +39 -0
- data/test/lib/definition/customization_test.rb +36 -0
- data/test/lib/definition/formatted_test.rb +75 -0
- data/test/lib/definition/indexed_test.rb +57 -0
- data/test/lib/definition/mappings_test.rb +26 -0
- data/test/lib/definition_test.rb +84 -0
- data/test/lib/dumper_test.rb +38 -0
- data/test/lib/format_test.rb +64 -0
- data/test/lib/has_attributes/json_reader_test.rb +46 -0
- data/test/lib/has_attributes_test.rb +69 -0
- data/test/lib/model_test.rb +44 -0
- data/test/lib/null_definition_test.rb +27 -0
- data/test/lib/parent_definition_test.rb +75 -0
- data/test/lib/provided_test.rb +46 -0
- data/test/lib/segmented_test.rb +27 -0
- data/test/lib/serializations/reflection_test.rb +24 -0
- data/test/lib/serializations/serialize_many_test.rb +194 -0
- data/test/lib/serializations/serialize_one_test.rb +122 -0
- data/test/lib/serializations_test.rb +24 -0
- data/test/lib/serialized_child_test.rb +91 -0
- data/test/lib/timestamp_attributes_test.rb +14 -0
- data/test/lib/validations_test.rb +8 -0
- data/test/lib/versioned_test.rb +35 -0
- data/test/models/attribute_customization/local_cache_test.rb +16 -0
- data/test/models/attribute_customization_test.rb +8 -0
- data/test/models/attribute_mapping/local_cache_test.rb +31 -0
- data/test/models/attribute_mapping_test.rb +20 -0
- data/test/support/business.rb +22 -0
- data/test/support/pg.rb +9 -0
- data/test/support/vegetable.rb +8 -0
- metadata +215 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
module AxleAttributes
|
|
2
|
+
module HasAttributes
|
|
3
|
+
class JsonReader
|
|
4
|
+
# json = {
|
|
5
|
+
# {
|
|
6
|
+
# "location": {
|
|
7
|
+
# "street": {"type": "string"},
|
|
8
|
+
# "city": {"type": "string", "readonly": true}
|
|
9
|
+
# }
|
|
10
|
+
# "group2": {
|
|
11
|
+
# },
|
|
12
|
+
# ...
|
|
13
|
+
# },
|
|
14
|
+
# "contacts": {
|
|
15
|
+
# "type": "serialize_many",
|
|
16
|
+
# "attributes": {
|
|
17
|
+
# "first_name": {"type": "string"},
|
|
18
|
+
# "professional_title": {"type": "string"}
|
|
19
|
+
# }
|
|
20
|
+
# }
|
|
21
|
+
# }
|
|
22
|
+
#
|
|
23
|
+
# AxleAttributes::HasAttributes::JsonReader.read_definitions(Widget, json)
|
|
24
|
+
#
|
|
25
|
+
def self.read_definitions(model, json, options = {})
|
|
26
|
+
new(model, options).read_definitions(json)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
attr_reader :model
|
|
30
|
+
attr_reader :reader_options
|
|
31
|
+
def initialize(model, reader_options = {})
|
|
32
|
+
@model = model
|
|
33
|
+
@reader_options = reader_options
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def read_definitions(json)
|
|
37
|
+
json = ActiveSupport::JSON.decode(json) unless json.is_a?(Hash)
|
|
38
|
+
|
|
39
|
+
json.each do |namespace, options|
|
|
40
|
+
if options['type']
|
|
41
|
+
read_serialization namespace, options
|
|
42
|
+
else
|
|
43
|
+
read_definition_group options
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def read_definition_group(definitions)
|
|
51
|
+
definitions.each do |attribute_name, options|
|
|
52
|
+
model.has_attribute attribute_name, normalize_options(options)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
SERIALIZATION_TYPES = %w(serialize_one serialize_many)
|
|
57
|
+
def read_serialization(serialization_name, options)
|
|
58
|
+
raise "unknown serialization #{options['type']}" unless SERIALIZATION_TYPES.include?(options['type'])
|
|
59
|
+
serialization_type = options['type']
|
|
60
|
+
serialization_attributes = options['attributes']
|
|
61
|
+
|
|
62
|
+
child_model = (options['class_name'] || serialization_name.classify).constantize
|
|
63
|
+
AxleAttributes::HasAttributes::JsonReader.read_definitions(child_model, {serialization_type => serialization_attributes}, reader_options)
|
|
64
|
+
reflection = model.send(serialization_type, serialization_name.to_sym, options.slice('class_name').symbolize_keys!)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
SYMBOL_OPTIONS = [:type, :format, :index, :mmdb_name]
|
|
68
|
+
def normalize_options(options)
|
|
69
|
+
options.symbolize_keys!
|
|
70
|
+
|
|
71
|
+
SYMBOL_OPTIONS.each do |option_name|
|
|
72
|
+
if (value = options[option_name]).is_a?(String)
|
|
73
|
+
options[option_name] = value.to_sym
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
options.reverse_merge! default_options
|
|
78
|
+
|
|
79
|
+
options
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def default_options
|
|
83
|
+
@default_options ||= (reader_options[:defaults] || {})
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module AxleAttributes
|
|
2
|
+
module Model
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
included do
|
|
6
|
+
unless include?(ElasticRecord::Model)
|
|
7
|
+
include ElasticRecord::Model
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
include AxleAttributes::HasAttributes
|
|
11
|
+
self.has_attribute_definition_class = '::AxleAttributes::ParentDefinition'
|
|
12
|
+
add_core_axle_attributes! unless self.name.demodulize == 'Base'
|
|
13
|
+
|
|
14
|
+
include AxleAttributes::Provided
|
|
15
|
+
include AxleAttributes::Segmented
|
|
16
|
+
include AxleAttributes::Serializations
|
|
17
|
+
include AxleAttributes::Versioned
|
|
18
|
+
include AxleAttributes::Validations
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module ClassMethods
|
|
22
|
+
def inherited(child)
|
|
23
|
+
super
|
|
24
|
+
child.add_core_axle_attributes!
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def add_core_axle_attributes!
|
|
28
|
+
has_attribute(:created_at, type: :time, index: true, editable: false) unless attributes['created_at']
|
|
29
|
+
has_attribute(:updated_at, type: :time, index: true, editable: false) unless attributes['updated_at']
|
|
30
|
+
has_attribute(:segment_id, type: :integer, index: true, editable: false) unless attributes['segment_id']
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module AxleAttributes
|
|
2
|
+
class ParentDefinition < ::AxleAttributes::Definition
|
|
3
|
+
setup do
|
|
4
|
+
setup_storage!
|
|
5
|
+
setup_index!
|
|
6
|
+
setup_versioning!
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def setup_storage!
|
|
12
|
+
if type
|
|
13
|
+
storage_options = case type
|
|
14
|
+
when :text
|
|
15
|
+
{type: :string}
|
|
16
|
+
else
|
|
17
|
+
options.slice(:type, :default)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
model.attribute(name, storage_options)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def setup_index!
|
|
25
|
+
if mapping = index_mapping
|
|
26
|
+
model.elastic_index.mapping[:properties].merge!(name => mapping)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def setup_versioning!
|
|
31
|
+
if versioned?
|
|
32
|
+
model.add_versioned_attribute name
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module AxleAttributes
|
|
2
|
+
module Provided
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
include ActiveModel::AttributeMethods
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
attribute_method_suffix '_provided?'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module ClassMethods
|
|
11
|
+
def tracking_disabled?
|
|
12
|
+
Thread.current[:disable_provided_tracking]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def without_provided_tracking
|
|
16
|
+
Thread.current[:disable_provided_tracking] = true
|
|
17
|
+
yield
|
|
18
|
+
ensure
|
|
19
|
+
Thread.current[:disable_provided_tracking] = false
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def provided
|
|
24
|
+
provided_set.to_a
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def provided_set
|
|
28
|
+
@provided_set ||= Set.new
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def provided?(attr)
|
|
32
|
+
provided_set.include? attr
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def save(*)
|
|
36
|
+
if status = super
|
|
37
|
+
provided_set.clear
|
|
38
|
+
end
|
|
39
|
+
status
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Attempts to <tt>save!</tt> the record and clears changed attributes if successful.
|
|
43
|
+
def save!(*)
|
|
44
|
+
super.tap do
|
|
45
|
+
provided_set.clear
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# <tt>reload</tt> the record and clears changed attributes.
|
|
50
|
+
def reload(*)
|
|
51
|
+
super.tap do
|
|
52
|
+
provided_set.clear
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
def attribute_provided?(attr)
|
|
58
|
+
provided_set.include?(attr)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def write_attribute(attr, value)
|
|
62
|
+
provided_set << attr.to_s unless self.class.tracking_disabled?
|
|
63
|
+
|
|
64
|
+
super(attr, value)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'digest'
|
|
2
|
+
|
|
3
|
+
module AxleAttributes
|
|
4
|
+
module Segmented
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
class_attribute :max_segment
|
|
9
|
+
self.max_segment = 1024
|
|
10
|
+
|
|
11
|
+
before_create do
|
|
12
|
+
regenerate_segment_id unless segment_id
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
module ClassMethods
|
|
17
|
+
def segment_range
|
|
18
|
+
0..(max_segment-1)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def generate_segment_id
|
|
23
|
+
Digest::MD5.digest(id.to_s).unpack("L")[0] % max_segment
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def regenerate_segment_id
|
|
27
|
+
self.segment_id = generate_segment_id
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
module AxleAttributes
|
|
2
|
+
module Serializations
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
extend ActiveSupport::Autoload
|
|
5
|
+
|
|
6
|
+
autoload :Reflection, 'axle_attributes/serializations/reflection'
|
|
7
|
+
autoload :Serialization, 'axle_attributes/serializations/serialization'
|
|
8
|
+
autoload :SerializeMany, 'axle_attributes/serializations/serialize_many'
|
|
9
|
+
autoload :SerializeOne, 'axle_attributes/serializations/serialize_one'
|
|
10
|
+
|
|
11
|
+
module Builder
|
|
12
|
+
autoload :Serialization, 'axle_attributes/serializations/builder/serialization'
|
|
13
|
+
autoload :SerializeMany, 'axle_attributes/serializations/builder/serialize_many'
|
|
14
|
+
autoload :SerializeOne, 'axle_attributes/serializations/builder/serialize_one'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
included do
|
|
18
|
+
class_attribute :serialization_reflections
|
|
19
|
+
self.serialization_reflections = {}
|
|
20
|
+
|
|
21
|
+
validate :validate_serialized_objects
|
|
22
|
+
|
|
23
|
+
after_save { child_serialization_cache.clear }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
module ClassMethods
|
|
27
|
+
# serialize_many serializes a collection of objects into a single column.
|
|
28
|
+
#
|
|
29
|
+
# === Example
|
|
30
|
+
#
|
|
31
|
+
# class Factory
|
|
32
|
+
# serialize_many :widgets
|
|
33
|
+
# end
|
|
34
|
+
#
|
|
35
|
+
# This adds the following:
|
|
36
|
+
# <tt>factory.widgets_count</tt> - A counter cache of the number of widgets
|
|
37
|
+
# <tt>factory.widgets_updated_at</tt> - The last time a widget was modifed
|
|
38
|
+
# <tt>factory.widgets</tt> - A collection of widgets
|
|
39
|
+
#
|
|
40
|
+
# == Options
|
|
41
|
+
# [:class_name]
|
|
42
|
+
# The name of the class to instantiate the serialized objects as.
|
|
43
|
+
def serialize_many(name, options = {})
|
|
44
|
+
reflection = AxleAttributes::Serializations::Reflection.new(:serialize_many, name, options)
|
|
45
|
+
AxleAttributes::Serializations::Builder::SerializeMany.new(self, reflection).build!
|
|
46
|
+
reflection
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# serialize_one serializes an object into a single column.
|
|
50
|
+
#
|
|
51
|
+
# === Example
|
|
52
|
+
#
|
|
53
|
+
# class Factory
|
|
54
|
+
# serialize_one :manager
|
|
55
|
+
# end
|
|
56
|
+
#
|
|
57
|
+
# This adds the following:
|
|
58
|
+
# <tt>factory.manager</tt> - Read the manager.
|
|
59
|
+
# <tt>factory.manager=(value)</tt> - Write the manager.
|
|
60
|
+
#
|
|
61
|
+
# == Options
|
|
62
|
+
# [:class_name]
|
|
63
|
+
# The name of the class to instantiate the serialized objects as.
|
|
64
|
+
def serialize_one(name, options = {})
|
|
65
|
+
reflection = AxleAttributes::Serializations::Reflection.new(:serialize_one, name, options)
|
|
66
|
+
AxleAttributes::Serializations::Builder::SerializeOne.new(self, reflection).build!
|
|
67
|
+
reflection
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def serialized_many?(name)
|
|
71
|
+
serialization_reflections[name.to_sym].try :collection?
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def generated_serialized_object_methods
|
|
75
|
+
@generated_serialized_object_methods ||= begin
|
|
76
|
+
mod = const_set(:GeneratedSerializedObjectMethods, Module.new)
|
|
77
|
+
include mod
|
|
78
|
+
mod
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Returns the searches_many instance for the given name, instantiating it if it doesn't already exist
|
|
84
|
+
def child_serialization(name)
|
|
85
|
+
serialization = child_serialization_instance_get(name)
|
|
86
|
+
|
|
87
|
+
if serialization.nil?
|
|
88
|
+
reflection = serialization_reflections[name]
|
|
89
|
+
serialization = reflection.serialization_class.new(self, reflection)
|
|
90
|
+
child_serialization_instance_set(name, serialization)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
serialization
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def reload
|
|
97
|
+
super
|
|
98
|
+
child_serialization_cache.clear
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def validate_serialized_objects
|
|
102
|
+
child_serialization_cache.each do |name, serialization|
|
|
103
|
+
next unless serialization.loaded?
|
|
104
|
+
|
|
105
|
+
validation_method = serialization.reflection.collection? ? :validate_collection_serialization : :validate_single_serialization
|
|
106
|
+
send(validation_method, serialization)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def validate_collection_serialization(serialization)
|
|
111
|
+
serialization.reader.each do |record|
|
|
112
|
+
if record.invalid?
|
|
113
|
+
record.errors.each do |attribute, message|
|
|
114
|
+
attribute = "#{serialization.name}.#{attribute}"
|
|
115
|
+
errors[attribute] << message
|
|
116
|
+
errors[attribute].uniq!
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def validate_single_serialization(serialization)
|
|
123
|
+
record = serialization.reader
|
|
124
|
+
|
|
125
|
+
if record && record.invalid?
|
|
126
|
+
record.errors.each do |attribute, message|
|
|
127
|
+
attribute = "#{serialization.name}.#{attribute}"
|
|
128
|
+
errors[attribute] << message
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
def child_serialization_cache
|
|
136
|
+
@child_serialization_cache ||= {}
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def child_serialization_instance_get(name)
|
|
140
|
+
child_serialization_cache[name.to_sym]
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def child_serialization_instance_set(name, serialization)
|
|
144
|
+
child_serialization_cache[name.to_sym] = serialization
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
module AxleAttributes::Serializations::Builder
|
|
2
|
+
class Serialization
|
|
3
|
+
attr_accessor :model, :reflection
|
|
4
|
+
def initialize(model, reflection)
|
|
5
|
+
@model = model
|
|
6
|
+
@reflection = reflection
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def build!
|
|
10
|
+
model.serialization_reflections = model.serialization_reflections.merge(reflection.name => reflection)
|
|
11
|
+
|
|
12
|
+
define_attributes
|
|
13
|
+
define_writer
|
|
14
|
+
define_reader
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def define_attributes
|
|
18
|
+
model.attribute_definition_class.new(model, reflection.storage_column, type: :json, index: false, version: true).setup!
|
|
19
|
+
|
|
20
|
+
model.has_attributes(meta_attributes)
|
|
21
|
+
|
|
22
|
+
setup_definitions_on_parent
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def mixin
|
|
26
|
+
model.generated_serialized_object_methods
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def define_writer
|
|
30
|
+
name = reflection.name
|
|
31
|
+
|
|
32
|
+
mixin.redefine_method "#{name}=" do |records|
|
|
33
|
+
child_serialization(name).writer(records)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
mixin.redefine_method "#{name}_attributes=" do |attrs|
|
|
37
|
+
child_serialization(name).attributes_writer(attrs)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def define_reader
|
|
42
|
+
name = reflection.name
|
|
43
|
+
|
|
44
|
+
mixin.redefine_method(name) do
|
|
45
|
+
child_serialization(name).reader
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
mixin.redefine_method("#{name}_was") do
|
|
49
|
+
child_serialization(name).reader_was
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def setup_definitions_on_parent
|
|
54
|
+
serialized_index_mapping = {
|
|
55
|
+
type: index_type,
|
|
56
|
+
properties: {}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
reflection.klass.attributes.each_value do |definition|
|
|
60
|
+
definition.nested_path = reflection.name.to_s if reflection.collection?
|
|
61
|
+
|
|
62
|
+
model.add_attribute_definition("#{reflection.name}.#{definition.name}", definition)
|
|
63
|
+
|
|
64
|
+
if mapping = definition.index_mapping
|
|
65
|
+
serialized_index_mapping[:properties][definition.name] = mapping
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
model.elastic_index.mapping[:properties].merge!(reflection.name.to_s => serialized_index_mapping)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
protected
|
|
73
|
+
|
|
74
|
+
def index_type
|
|
75
|
+
raise "index_type must be implemented"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def meta_attributes
|
|
79
|
+
{}
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
end
|
|
83
|
+
end
|