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,34 @@
|
|
|
1
|
+
module AxleAttributes
|
|
2
|
+
class Definition
|
|
3
|
+
# Adds support for +setup+ and +teardown+ callbacks.
|
|
4
|
+
# These callbacks serve as a replacement to overwriting the
|
|
5
|
+
# <tt>#setup</tt> and <tt>#teardown</tt> methods of your TestCase.
|
|
6
|
+
#
|
|
7
|
+
# class ExampleTest < ActiveSupport::TestCase
|
|
8
|
+
# setup do
|
|
9
|
+
# # ...
|
|
10
|
+
# end
|
|
11
|
+
# end
|
|
12
|
+
module Callbacks
|
|
13
|
+
|
|
14
|
+
extend ActiveSupport::Concern
|
|
15
|
+
|
|
16
|
+
included do
|
|
17
|
+
include ActiveSupport::Callbacks
|
|
18
|
+
define_callbacks :setup
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module ClassMethods
|
|
22
|
+
# Add a callback, which runs before <tt>TestCase#setup</tt>.
|
|
23
|
+
def setup(*args, &block)
|
|
24
|
+
set_callback(:setup, :before, *args, &block)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def before_setup # :nodoc:
|
|
29
|
+
super
|
|
30
|
+
run_callbacks :setup
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module AxleAttributes
|
|
2
|
+
class Definition
|
|
3
|
+
module Customization
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
class_attribute :cached_customizations
|
|
8
|
+
self.cached_customizations = {}
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module ClassMethods
|
|
12
|
+
def cached_customization_for(namespace)
|
|
13
|
+
cached_customizations[namespace] ||= AttributeCustomization.namespace_cache_for(namespace).read
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def customization
|
|
18
|
+
self.class.cached_customization_for(namespace)[name]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def display_name
|
|
22
|
+
read_customization(:display_name) || name.titlecase.gsub('.', ' ')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def description
|
|
26
|
+
read_customization :description
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def notes
|
|
30
|
+
read_customization :notes
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def editable_customization
|
|
34
|
+
value = customization.try(:editable)
|
|
35
|
+
value.nil? ? true : value
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def deprecated_on
|
|
39
|
+
read_customization :deprecated_on
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def deprecated?
|
|
43
|
+
deprecated_on.present?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def read_customization(property)
|
|
47
|
+
customization.try(property).presence
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module AxleAttributes
|
|
2
|
+
class Definition
|
|
3
|
+
module Formatted
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
class_attribute :formatters
|
|
8
|
+
self.formatters = {}
|
|
9
|
+
|
|
10
|
+
setup do
|
|
11
|
+
setup_method_override!
|
|
12
|
+
formatter.try(:setup!, self)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def analyzer_mapping
|
|
17
|
+
if mapping = formatter.try(:analyzer_mapping)
|
|
18
|
+
mapping
|
|
19
|
+
else
|
|
20
|
+
super
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def instructions
|
|
25
|
+
fetch_format_property(:instructions)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def standardize(value)
|
|
29
|
+
formatter.try(:standardize, self, value) || value
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def validate(value)
|
|
33
|
+
formatter.try :validate, self, value
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def formatter
|
|
37
|
+
formatters[format]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def fetch_format_property(property)
|
|
43
|
+
read_customization(property) || formatter.try(property, self)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def setup_method_override!
|
|
47
|
+
if formatter.respond_to?(:standardize)
|
|
48
|
+
definition = self
|
|
49
|
+
model.send :define_method, "#{name}=" do |value|
|
|
50
|
+
super definition.standardize(value)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module AxleAttributes
|
|
2
|
+
class Definition
|
|
3
|
+
module Indexed
|
|
4
|
+
def index
|
|
5
|
+
options[:index]
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
ANALYZERS = [:snowball, :proper_noun, :simple]
|
|
9
|
+
def analyzer_mapping
|
|
10
|
+
{type: 'string', analyzer: index} if ANALYZERS.include?(index)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
INDEXES_BY_TYPE = {
|
|
14
|
+
array: {type: "string", index: "not_analyzed", doc_values: true},
|
|
15
|
+
boolean: {type: "boolean", doc_values: true},
|
|
16
|
+
float: {type: "double", doc_values: true},
|
|
17
|
+
integer: {type: "long", doc_values: true},
|
|
18
|
+
json: {type: "boolean"},
|
|
19
|
+
string: {type: 'string', index_analyzer: 'keyword', search_analyzer: 'standard'},
|
|
20
|
+
text: {type: 'string', index_analyzer: 'keyword', search_analyzer: 'standard'},
|
|
21
|
+
time: {type: "date", format: "dateOptionalTime", doc_values: true},
|
|
22
|
+
date: {type: "date", format: "date", doc_values: true}
|
|
23
|
+
}
|
|
24
|
+
def index_mapping
|
|
25
|
+
return unless index
|
|
26
|
+
|
|
27
|
+
if mapping = analyzer_mapping
|
|
28
|
+
multi_field_index(name, mapping)
|
|
29
|
+
elsif index == true
|
|
30
|
+
INDEXES_BY_TYPE[type] || (raise "unknown index type #{type.inspect} for #{name}")
|
|
31
|
+
elsif index.is_a?(Hash)
|
|
32
|
+
index
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def multi_field_index(field, analyzer_mapping)
|
|
37
|
+
{type: "multi_field", fields: {field => {type: "string", index: "not_analyzed", doc_values: true}, analyzed: analyzer_mapping}}
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module AxleAttributes
|
|
2
|
+
class Definition
|
|
3
|
+
module Mappings
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
class_attribute :cached_attribute_mappings
|
|
8
|
+
self.cached_attribute_mappings = {}
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module ClassMethods
|
|
12
|
+
def cached_attribute_mappings_for(namespace)
|
|
13
|
+
cached_attribute_mappings[namespace] ||= AttributeMapping.cached_mapping_for(namespace).read
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_options
|
|
18
|
+
mappings.map do |k, v|
|
|
19
|
+
[v, k]
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def mappings
|
|
24
|
+
if @mappings
|
|
25
|
+
@mappings
|
|
26
|
+
elsif mapping_model && mapping_model.respond_to?(:cached_mapping)
|
|
27
|
+
mapping_model.cached_mapping
|
|
28
|
+
else
|
|
29
|
+
self.class.cached_attribute_mappings_for(namespace)[name] || {}
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def mappings=(val)
|
|
34
|
+
@mappings = val
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def mapping_records
|
|
38
|
+
AttributeMapping.where(namespace: namespace, attribute_name: name)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def mapping_model
|
|
42
|
+
if options[:mapping_model]
|
|
43
|
+
@mapping_model ||= options[:mapping_model].constantize
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require 'csv'
|
|
2
|
+
|
|
3
|
+
module AxleAttributes
|
|
4
|
+
class Dumper
|
|
5
|
+
attr_reader :model
|
|
6
|
+
def initialize(model)
|
|
7
|
+
@model = model
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def dump_internal
|
|
11
|
+
dump(model.attribute_set, ["Attribute", "Display Name", "Type", "Deprecation", "Legacy Name", "Mapping", "Description", "Note"]) do |definition|
|
|
12
|
+
[
|
|
13
|
+
definition.canonical_name,
|
|
14
|
+
definition.display_name,
|
|
15
|
+
definition.type,
|
|
16
|
+
definition.deprecated_on ? definition.deprecated_on.to_s('%Y/%m/%d') : nil,
|
|
17
|
+
definition.options[:mmdb_name],
|
|
18
|
+
mapping_display(definition),
|
|
19
|
+
definition.description,
|
|
20
|
+
definition.notes
|
|
21
|
+
]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def dump_public(attribute_set)
|
|
26
|
+
dump(attribute_set, ["Attribute", "Display Name", "Type", "Description"]) do |definition|
|
|
27
|
+
[
|
|
28
|
+
definition.canonical_name,
|
|
29
|
+
definition.display_name,
|
|
30
|
+
definition.type,
|
|
31
|
+
definition.description
|
|
32
|
+
]
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def dump(attribute_set, headers)
|
|
37
|
+
CSV.generate(headers: headers, write_headers: true) do |csv|
|
|
38
|
+
attribute_set.sort.each do |attribute_name|
|
|
39
|
+
definition = model.attributes[attribute_name]
|
|
40
|
+
csv << yield(definition) if definition
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def mapping_display(definition)
|
|
46
|
+
if mapping_model = definition.mapping_model
|
|
47
|
+
"#{mapping_model.model_name.plural}.#{mapping_model.natural_key}" if mapping_model.respond_to?(:natural_key)
|
|
48
|
+
elsif definition.mappings.any?
|
|
49
|
+
"attribute_mappings"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module AxleAttributes
|
|
2
|
+
class Format
|
|
3
|
+
class_attribute :phone_regex
|
|
4
|
+
self.phone_regex = /\A\(\d{3}\) \d{3}-\d{4}\z/
|
|
5
|
+
|
|
6
|
+
class_attribute :url_regex
|
|
7
|
+
self.url_regex = /\A(https?:\/\/)([-a-z0-9]+\.[-.a-z0-9:]+)(\/[^\s]+)?\z/
|
|
8
|
+
|
|
9
|
+
class_attribute :zip_regex
|
|
10
|
+
self.zip_regex = /\A\d{5}\z/
|
|
11
|
+
|
|
12
|
+
class_attribute :postal_code_regex
|
|
13
|
+
self.postal_code_regex = /\A(\d{5}(-\d{4})?|[A-Z0-9]{6})\z/
|
|
14
|
+
|
|
15
|
+
class << self
|
|
16
|
+
PHONEWORDS = {"A"=>"2", "B"=>"2", "C"=>"2", "D"=>"3", "E"=>"3", "F"=>"3", "G"=>"4", "H"=>"4", "I"=>"4", "J"=>"5",
|
|
17
|
+
"K"=>"5", "L"=>"5", "M"=>"6", "N"=>"6", "O"=>"6", "P"=>"7", "Q"=>"7", "R"=>"7", "S"=>"7", "T"=>"8",
|
|
18
|
+
"U"=>"8", "V"=>"8", "W"=>"9", "X"=>"9", "Y"=>"9", "Z"=>"9"}
|
|
19
|
+
|
|
20
|
+
def standardize_phone(phone)
|
|
21
|
+
standardize phone, self.phone_regex do
|
|
22
|
+
standardized = phone.chars.map { |char| PHONEWORDS[char.upcase] || char }.join('')
|
|
23
|
+
|
|
24
|
+
standardized.gsub!(/\D/, '')
|
|
25
|
+
|
|
26
|
+
if standardized.size == 10
|
|
27
|
+
standardized.gsub!(/(\d{3})(\d{3})(\d{4})/, "(\\1) \\2-\\3")
|
|
28
|
+
standardized
|
|
29
|
+
elsif standardized.size == 11 && standardized[0] == '1'
|
|
30
|
+
standardized.gsub!(/1(\d{3})(\d{3})(\d{4})/, "(\\1) \\2-\\3")
|
|
31
|
+
standardized
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def standardize_postal_code(postal_code)
|
|
37
|
+
standardize postal_code, postal_code_regex do
|
|
38
|
+
standardized = postal_code.remove(/[^\-[:alnum:]]/)
|
|
39
|
+
case standardized.length
|
|
40
|
+
when 6 then standardized.upcase!
|
|
41
|
+
when 9 then standardized.sub!(/\A(\d{5})(\d{4})\z/, '\\1-\\2')
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
standardized
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def standardize_url(website)
|
|
49
|
+
standardize website, self.url_regex do
|
|
50
|
+
if website =~ /^(\w*):\/\//
|
|
51
|
+
scheme, _, resource = website.partition("://")
|
|
52
|
+
scheme.downcase!
|
|
53
|
+
else
|
|
54
|
+
scheme = "http"
|
|
55
|
+
resource = website
|
|
56
|
+
end
|
|
57
|
+
resource.strip!
|
|
58
|
+
|
|
59
|
+
host, slash, path = resource.partition("/")
|
|
60
|
+
host.downcase!
|
|
61
|
+
path.gsub! ' ', '%20'
|
|
62
|
+
|
|
63
|
+
path.empty? ? "#{scheme}://#{host}" : "#{scheme}://#{host}/#{path}"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def standardize(value, regex)
|
|
68
|
+
return value if value.blank? || value =~ regex
|
|
69
|
+
standardized = yield(value)
|
|
70
|
+
standardized =~ regex ? standardized : value
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
require 'axle_attributes/has_attributes/json_reader'
|
|
2
|
+
require 'open-uri'
|
|
3
|
+
|
|
4
|
+
module AxleAttributes
|
|
5
|
+
module HasAttributes
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
included do
|
|
9
|
+
class_attribute :has_attribute_definition_class
|
|
10
|
+
self.has_attribute_definition_class = '::AxleAttributes::Definition'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
module ClassMethods
|
|
14
|
+
def inherited(child)
|
|
15
|
+
child.attribute_set = attribute_set.dup
|
|
16
|
+
child.attributes = attributes.dup
|
|
17
|
+
super
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Example:
|
|
21
|
+
# has_attributes(
|
|
22
|
+
# color: {type: :string, index: true, version: true},
|
|
23
|
+
# weight: {type: :integer, version: true}
|
|
24
|
+
# )
|
|
25
|
+
def has_attributes(definitions, default_options = {})
|
|
26
|
+
definitions.each do |name, options|
|
|
27
|
+
has_attribute name, default_options.merge(options)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def has_attribute(name, options = {})
|
|
32
|
+
name = name.to_s
|
|
33
|
+
definition = attribute_definition_class.new(self, name, options)
|
|
34
|
+
add_attribute_definition(name, definition)
|
|
35
|
+
definition.setup!
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def add_attribute_definition(name, definition)
|
|
39
|
+
attributes[name] = definition
|
|
40
|
+
attribute_set << name
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def has_mappings(mappings_hash)
|
|
44
|
+
mappings_hash.each do |name, mappings|
|
|
45
|
+
has_mapping name, mappings
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def has_mapping(name, mappings)
|
|
50
|
+
if mappings.is_a?(Array)
|
|
51
|
+
mappings = Hash[mappings.map { |v| [v, v.humanize] }]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
attributes[name.to_s].mappings = mappings
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def attributes_file_root
|
|
58
|
+
@attributes_file_root ||= Rails.root.join('db', 'attributes')
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def attributes_file_root=(pathname)
|
|
62
|
+
pathname = Pathname.new(pathname) unless pathname.is_a?(Pathname)
|
|
63
|
+
@attributes_file_root = pathname
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def has_attributes_file(filename, options = {})
|
|
67
|
+
options[:defaults] ||= {}
|
|
68
|
+
options[:defaults][:filename] = filename
|
|
69
|
+
AxleAttributes::HasAttributes::JsonReader.read_definitions(self, read_attributes_file(filename), options)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def attributes
|
|
73
|
+
@attributes ||= {}
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def attributes=(attributes)
|
|
77
|
+
@attributes = attributes
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def attribute_set
|
|
81
|
+
@attribute_set ||= Set.new
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def attribute_set=(attribute_set)
|
|
85
|
+
@attribute_set = attribute_set
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def attribute_definition_class
|
|
89
|
+
@attribute_definition_class ||= has_attribute_definition_class.constantize
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
def read_attributes_file(filename)
|
|
95
|
+
attributes_file_pathname(filename).read
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def attributes_file_pathname(filename)
|
|
99
|
+
attributes_file_root.join("#{filename}.json")
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|