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.
Files changed (119) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.travis.yml +16 -0
  4. data/Gemfile +15 -0
  5. data/README.md +10 -0
  6. data/Rakefile +18 -0
  7. data/app/models/attribute_customization.rb +7 -0
  8. data/app/models/attribute_customization/local_cache.rb +28 -0
  9. data/app/models/attribute_mapping.rb +15 -0
  10. data/app/models/attribute_mapping/local_cache.rb +40 -0
  11. data/axle_attributes.gemspec +22 -0
  12. data/bin/rails +8 -0
  13. data/db/migrate/20131118042651_create_attribute_customizations.rb +16 -0
  14. data/db/migrate/20131121013237_create_attribute_mappings.rb +17 -0
  15. data/db/migrate/20151120160522_add_editable_to_attribute_customizations.rb +7 -0
  16. data/lib/axle_attributes.rb +30 -0
  17. data/lib/axle_attributes/child_definition.rb +42 -0
  18. data/lib/axle_attributes/conversions.rb +36 -0
  19. data/lib/axle_attributes/definition.rb +118 -0
  20. data/lib/axle_attributes/definition/callbacks.rb +34 -0
  21. data/lib/axle_attributes/definition/customization.rb +51 -0
  22. data/lib/axle_attributes/definition/formatted.rb +57 -0
  23. data/lib/axle_attributes/definition/indexed.rb +41 -0
  24. data/lib/axle_attributes/definition/mappings.rb +48 -0
  25. data/lib/axle_attributes/dumper.rb +54 -0
  26. data/lib/axle_attributes/engine.rb +4 -0
  27. data/lib/axle_attributes/format.rb +74 -0
  28. data/lib/axle_attributes/has_attributes.rb +104 -0
  29. data/lib/axle_attributes/has_attributes/json_reader.rb +87 -0
  30. data/lib/axle_attributes/model.rb +34 -0
  31. data/lib/axle_attributes/null_definition.rb +11 -0
  32. data/lib/axle_attributes/parent_definition.rb +37 -0
  33. data/lib/axle_attributes/provided.rb +67 -0
  34. data/lib/axle_attributes/regex.rb +4 -0
  35. data/lib/axle_attributes/segmented.rb +30 -0
  36. data/lib/axle_attributes/serializations.rb +147 -0
  37. data/lib/axle_attributes/serializations/builder/serialization.rb +83 -0
  38. data/lib/axle_attributes/serializations/builder/serialize_many.rb +17 -0
  39. data/lib/axle_attributes/serializations/builder/serialize_one.rb +7 -0
  40. data/lib/axle_attributes/serializations/reflection.rb +40 -0
  41. data/lib/axle_attributes/serializations/serialization.rb +88 -0
  42. data/lib/axle_attributes/serializations/serialize_many.rb +88 -0
  43. data/lib/axle_attributes/serializations/serialize_one.rb +34 -0
  44. data/lib/axle_attributes/serialized_child.rb +58 -0
  45. data/lib/axle_attributes/validations.rb +29 -0
  46. data/lib/axle_attributes/versioned.rb +21 -0
  47. data/test/dummy/README.rdoc +6 -0
  48. data/test/dummy/Rakefile +6 -0
  49. data/test/dummy/app/assets/images/.keep +0 -0
  50. data/test/dummy/app/assets/javascripts/application.js +13 -0
  51. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  52. data/test/dummy/app/controllers/application_controller.rb +5 -0
  53. data/test/dummy/app/controllers/concerns/.keep +0 -0
  54. data/test/dummy/app/helpers/application_helper.rb +2 -0
  55. data/test/dummy/app/mailers/.keep +0 -0
  56. data/test/dummy/app/models/.keep +0 -0
  57. data/test/dummy/app/models/concerns/.keep +0 -0
  58. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  59. data/test/dummy/bin/bundle +3 -0
  60. data/test/dummy/bin/rails +4 -0
  61. data/test/dummy/bin/rake +4 -0
  62. data/test/dummy/config.ru +4 -0
  63. data/test/dummy/config/application.rb +12 -0
  64. data/test/dummy/config/boot.rb +5 -0
  65. data/test/dummy/config/database.yml +13 -0
  66. data/test/dummy/config/environment.rb +5 -0
  67. data/test/dummy/config/environments/development.rb +29 -0
  68. data/test/dummy/config/environments/production.rb +80 -0
  69. data/test/dummy/config/environments/test.rb +38 -0
  70. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  71. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  72. data/test/dummy/config/initializers/inflections.rb +16 -0
  73. data/test/dummy/config/initializers/mime_types.rb +5 -0
  74. data/test/dummy/config/initializers/secret_token.rb +12 -0
  75. data/test/dummy/config/initializers/session_store.rb +3 -0
  76. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  77. data/test/dummy/config/locales/en.yml +23 -0
  78. data/test/dummy/config/routes.rb +56 -0
  79. data/test/dummy/db/schema.rb +50 -0
  80. data/test/dummy/lib/assets/.keep +0 -0
  81. data/test/dummy/log/.keep +0 -0
  82. data/test/dummy/public/404.html +58 -0
  83. data/test/dummy/public/422.html +58 -0
  84. data/test/dummy/public/500.html +57 -0
  85. data/test/dummy/public/favicon.ico +0 -0
  86. data/test/factories/all.rb +14 -0
  87. data/test/helper.rb +25 -0
  88. data/test/lib/child_definition_test.rb +43 -0
  89. data/test/lib/conversions_test.rb +39 -0
  90. data/test/lib/definition/customization_test.rb +36 -0
  91. data/test/lib/definition/formatted_test.rb +75 -0
  92. data/test/lib/definition/indexed_test.rb +57 -0
  93. data/test/lib/definition/mappings_test.rb +26 -0
  94. data/test/lib/definition_test.rb +84 -0
  95. data/test/lib/dumper_test.rb +38 -0
  96. data/test/lib/format_test.rb +64 -0
  97. data/test/lib/has_attributes/json_reader_test.rb +46 -0
  98. data/test/lib/has_attributes_test.rb +69 -0
  99. data/test/lib/model_test.rb +44 -0
  100. data/test/lib/null_definition_test.rb +27 -0
  101. data/test/lib/parent_definition_test.rb +75 -0
  102. data/test/lib/provided_test.rb +46 -0
  103. data/test/lib/segmented_test.rb +27 -0
  104. data/test/lib/serializations/reflection_test.rb +24 -0
  105. data/test/lib/serializations/serialize_many_test.rb +194 -0
  106. data/test/lib/serializations/serialize_one_test.rb +122 -0
  107. data/test/lib/serializations_test.rb +24 -0
  108. data/test/lib/serialized_child_test.rb +91 -0
  109. data/test/lib/timestamp_attributes_test.rb +14 -0
  110. data/test/lib/validations_test.rb +8 -0
  111. data/test/lib/versioned_test.rb +35 -0
  112. data/test/models/attribute_customization/local_cache_test.rb +16 -0
  113. data/test/models/attribute_customization_test.rb +8 -0
  114. data/test/models/attribute_mapping/local_cache_test.rb +31 -0
  115. data/test/models/attribute_mapping_test.rb +20 -0
  116. data/test/support/business.rb +22 -0
  117. data/test/support/pg.rb +9 -0
  118. data/test/support/vegetable.rb +8 -0
  119. metadata +215 -0
@@ -0,0 +1,17 @@
1
+ module AxleAttributes::Serializations::Builder
2
+ class SerializeMany < Serialization
3
+ def index_type
4
+ :nested
5
+ end
6
+
7
+ def meta_attributes
8
+ if reflection.counter_cache_column
9
+ {
10
+ reflection.counter_cache_column => {type: :integer, index: true, version: false, editable: false}
11
+ }
12
+ else
13
+ {}
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ module AxleAttributes::Serializations::Builder
2
+ class SerializeOne < Serialization
3
+ def index_type
4
+ :object
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,40 @@
1
+ module AxleAttributes
2
+ module Serializations
3
+ class Reflection
4
+ attr_accessor :macro, :name, :options
5
+ def initialize(macro, name, options)
6
+ @macro = macro
7
+ @name = name.to_sym
8
+ @options = options
9
+ @collection = macro == :serialize_many
10
+ end
11
+
12
+ def serialization_class
13
+ case macro
14
+ when :serialize_many
15
+ Serializations::SerializeMany
16
+ when :serialize_one
17
+ Serializations::SerializeOne
18
+ end
19
+ end
20
+
21
+ def storage_column
22
+ name
23
+ end
24
+
25
+ def counter_cache_column
26
+ unless options[:counter_cache] == false
27
+ "#{name}_count"
28
+ end
29
+ end
30
+
31
+ def klass
32
+ @klass ||= (options[:class_name] || name.to_s.classify).constantize
33
+ end
34
+
35
+ def collection?
36
+ @collection
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,88 @@
1
+ module AxleAttributes
2
+ module Serializations
3
+ class Serialization
4
+ attr_accessor :owner, :reflection
5
+ def initialize(owner, reflection)
6
+ @owner = owner
7
+ @reflection = reflection
8
+ @collection = nil
9
+ @loaded = false
10
+ end
11
+
12
+ def name
13
+ reflection.name
14
+ end
15
+
16
+ def writer(value)
17
+ raise "writer must be implemented"
18
+ end
19
+
20
+ def reader
21
+ target
22
+ end
23
+
24
+ def reader_was
25
+ if owner.changed_attributes.include?(name.to_s)
26
+ instantiate_target_with_self owner.changed_attributes[name.to_s]
27
+ else
28
+ target
29
+ end
30
+ end
31
+
32
+ def loaded?
33
+ @loaded
34
+ end
35
+
36
+ protected
37
+
38
+ def target=(value)
39
+ @target = value
40
+ target_with_serialization_instance(@target)
41
+ loaded!
42
+ owner[reflection.storage_column] = mutate_json_times!(value.as_json)
43
+ end
44
+
45
+ def mutate_json_times!(json)
46
+ Array.wrap(json).each do |attrs|
47
+ attrs.each do |k, v|
48
+ if v.acts_like?(:time) || v.acts_like?(:date)
49
+ attrs[k] = v.as_json
50
+ end
51
+ end
52
+ end
53
+
54
+ json
55
+ end
56
+
57
+ def target
58
+ unless loaded?
59
+ @target = load_target
60
+ loaded!
61
+ end
62
+
63
+ @target
64
+ end
65
+
66
+ def loaded!
67
+ @loaded = true
68
+ end
69
+
70
+ def load_target
71
+ instantiate_target_with_self owner[reflection.storage_column]
72
+ end
73
+
74
+ def instantiate_target_with_self(json)
75
+ target_with_serialization_instance instantiate_target(json)
76
+ end
77
+
78
+ def target_with_serialization_instance(target)
79
+ Array.wrap(target).each { |record| record.serialization_instance = self }
80
+ target
81
+ end
82
+
83
+ def instantiate_target(json)
84
+ raise "instantiate_target must be implemented"
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,88 @@
1
+ module AxleAttributes
2
+ module Serializations
3
+ class SerializeMany < Serialization
4
+ def writer(records)
5
+ return if records.nil? && owner[reflection.storage_column].nil?
6
+
7
+ records ||= []
8
+ records = records.map { |record| record.is_a?(Hash) ? reflection.klass.new(record) : record }
9
+
10
+ replace(records)
11
+ end
12
+
13
+ def replace(records)
14
+ delete(target - records)
15
+ concat(records)
16
+
17
+ target_changed!
18
+ end
19
+
20
+ def delete(records)
21
+ records.each { |record| delete_from_collection(record) }
22
+ end
23
+
24
+ def concat(records)
25
+ records.each { |record| add_to_collection(record) }
26
+ end
27
+
28
+ def attributes_writer(attributes_collection)
29
+ return if attributes_collection.empty?
30
+
31
+ if attributes_collection.is_a? Hash
32
+ attributes_collection = attributes_collection.values
33
+ end
34
+
35
+ attributes_collection.each do |attributes|
36
+ attributes = attributes.with_indifferent_access
37
+
38
+ if attributes['_destroy'] == '1'
39
+ target.delete_if { |record| record.id == attributes['id'] }
40
+ else
41
+ if existing = target.detect { |record| record.id == attributes['id'] }
42
+ existing.attributes = attributes
43
+ else
44
+ target << reflection.klass.new(attributes)
45
+ end
46
+ end
47
+ end
48
+
49
+ target_changed!
50
+ end
51
+
52
+ private
53
+
54
+ def target_changed!
55
+ self.target = target
56
+
57
+ if reflection.counter_cache_column
58
+ owner.send("#{reflection.counter_cache_column}=", target.size)
59
+ end
60
+ end
61
+
62
+ def instantiate_target(json)
63
+ json.nil? ? [] : json.map { |entry| reflection.klass.instantiate(entry['id'], entry) }
64
+ end
65
+
66
+ def delete_from_collection(record)
67
+ target.delete(record)
68
+ end
69
+
70
+ def add_to_collection(record)
71
+ if index = target.index(record)
72
+ current = target[index]
73
+ new_attributes = record.attributes.update('id' => current.id, 'created_at' => current.created_at)
74
+
75
+ current.attributes.each_key do |key|
76
+ unless new_attributes.key?(key)
77
+ current.send("#{key}=", reflection.klass.attributes[key].default)
78
+ end
79
+ end
80
+
81
+ current.attributes = new_attributes
82
+ else
83
+ target << record
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,34 @@
1
+ module AxleAttributes
2
+ module Serializations
3
+ class SerializeOne < Serialization
4
+ def writer(record_or_hash)
5
+ if record_or_hash.is_a?(Hash)
6
+ if target
7
+ target.attributes = record_or_hash
8
+ record = target
9
+ else
10
+ record = reflection.klass.new(record_or_hash)
11
+ end
12
+ else
13
+ record = record_or_hash
14
+ end
15
+
16
+ self.target = record
17
+ end
18
+
19
+ def attributes_writer(attributes)
20
+ attributes = attributes.with_indifferent_access
21
+ writer(attributes['_destroy'] == '1' ? nil : attributes)
22
+ end
23
+
24
+ private
25
+
26
+ def instantiate_target(json)
27
+ if json
28
+ reflection.klass.instantiate json['id'], json
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,58 @@
1
+ require 'securerandom'
2
+
3
+ module AxleAttributes
4
+ module SerializedChild
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ include SuperstoreOverrides
9
+ alias :eql? :==
10
+
11
+ include AxleAttributes::Versioned
12
+ include AxleAttributes::HasAttributes
13
+ self.has_attribute_definition_class = '::AxleAttributes::ChildDefinition'
14
+ has_attribute :id, type: :string, index: true, editable: false, version: true
15
+ has_attribute :created_at, type: :time, index: true, editable: false
16
+
17
+ attr_accessor :serialization_instance
18
+
19
+ include AxleAttributes::Provided
20
+ include AxleAttributes::Validations
21
+ end
22
+
23
+ module SuperstoreOverrides
24
+ def initialize(*args)
25
+ super
26
+ @attributes['created_at'] ||= Time.current
27
+ end
28
+
29
+ def attributes=(params)
30
+ params.each do |attr, value|
31
+ self.public_send("#{attr}=", value) if respond_to?("#{attr}=")
32
+ end
33
+ end
34
+
35
+ def ==(other)
36
+ super || hash == other.hash
37
+ end
38
+
39
+ def attributes
40
+ result = super
41
+ result.select! { |attr, value| !value.nil? && self.class.attributes[attr] }
42
+ result
43
+ end
44
+ end
45
+
46
+ def hash
47
+ id.hash
48
+ end
49
+
50
+ def versioned_attributes
51
+ attributes.keep_if { |key, value| self.class.attributes[key].versioned? }
52
+ end
53
+
54
+ def as_search
55
+ attributes.keep_if { |key, value| self.class.attributes[key].indexed? }
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,29 @@
1
+ module AxleAttributes
2
+ module Validations
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ validate :validate_formats
7
+ end
8
+
9
+ private
10
+
11
+ def validate_formats
12
+ each_provided_definition do |definition|
13
+ value = send(definition.name)
14
+ if value.present? && error = definition.validate(value)
15
+ errors.add(definition.name, error)
16
+ end
17
+ end
18
+ end
19
+
20
+ def each_provided_definition
21
+ provided_set.each do |attr|
22
+ if definition = self.class.attributes[attr]
23
+ yield definition
24
+ end
25
+ end
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,21 @@
1
+ module AxleAttributes
2
+ module Versioned
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ class_attribute :versioned_attributes, instance_accessor: false
7
+ self.versioned_attributes = Set.new
8
+ end
9
+
10
+ module ClassMethods
11
+ def inherited(child)
12
+ child.versioned_attributes = versioned_attributes.dup
13
+ super
14
+ end
15
+
16
+ def add_versioned_attribute(*attributes)
17
+ attributes.each { |attribute| versioned_attributes << attribute.to_s }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,6 @@
1
+ == README
2
+
3
+ Running tests
4
+
5
+ rake db:create
6
+ rake
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require File.expand_path('../config/application', __FILE__)
5
+
6
+ Dummy::Application.load_tasks
File without changes
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file.
9
+ //
10
+ // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,5 @@
1
+ class ApplicationController < ActionController::Base
2
+ # Prevent CSRF attacks by raising an exception.
3
+ # For APIs, you may want to use :null_session instead.
4
+ protect_from_forgery with: :exception
5
+ end