countrizable 0.1.1
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/Gemfile +15 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +27 -0
- data/lib/countrizable/active_record/act_macro.rb +116 -0
- data/lib/countrizable/active_record/adapter.rb +108 -0
- data/lib/countrizable/active_record/adapter_dirty.rb +56 -0
- data/lib/countrizable/active_record/attributes.rb +26 -0
- data/lib/countrizable/active_record/class_methods.rb +129 -0
- data/lib/countrizable/active_record/country_attributes_query.rb +181 -0
- data/lib/countrizable/active_record/country_value.rb +45 -0
- data/lib/countrizable/active_record/exceptions.rb +13 -0
- data/lib/countrizable/active_record/instance_methods.rb +246 -0
- data/lib/countrizable/active_record/migration.rb +215 -0
- data/lib/countrizable/active_record.rb +14 -0
- data/lib/countrizable/i18n/country_code.rb +9 -0
- data/lib/countrizable/i18n.rb +5 -0
- data/lib/countrizable/interpolation.rb +28 -0
- data/lib/countrizable/railtie.rb +4 -0
- data/lib/countrizable/version.rb +3 -0
- data/lib/countrizable.rb +95 -0
- data/lib/i18n/country_code.rb +7 -0
- data/lib/patches/active_record/persistence.rb +17 -0
- data/lib/patches/active_record/query_method.rb +3 -0
- data/lib/patches/active_record/rails4/query_method.rb +35 -0
- data/lib/patches/active_record/rails4/serialization.rb +22 -0
- data/lib/patches/active_record/rails4/uniqueness_validator.rb +42 -0
- data/lib/patches/active_record/rails5/uniqueness_validator.rb +47 -0
- data/lib/patches/active_record/rails5_1/serialization.rb +22 -0
- data/lib/patches/active_record/rails5_1/uniqueness_validator.rb +45 -0
- data/lib/patches/active_record/relation.rb +12 -0
- data/lib/patches/active_record/serialization.rb +5 -0
- data/lib/patches/active_record/uniqueness_validator.rb +7 -0
- data/lib/patches/active_record/xml_attribute_serializer.rb +23 -0
- data/lib/tasks/countrizable_tasks.rake +4 -0
- metadata +259 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: '039886e40e62d429443e1ade142f7ed8a6b5b3e83a12b7a7fca4fa109b5b3aff'
|
4
|
+
data.tar.gz: 51e4c3986a33eef57b917eceaa7fd6fb882ffcaabd66ff796fa2e33e72fd48bc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d76198119c24f64c9c77b27bd0c45d77c224001f2c443b8ffacc1e8913406fbb09ae72b4c115c28ba46e98fa04f320389b3f8b1b735fd8c14d90ed083fb7eba1
|
7
|
+
data.tar.gz: 0ffc9b2df18f3d71f878f2ddde6eaea2f4c0d65a33aeb67dd5965ca4a704b8bdba8866be1bf997f63eb864a035bd05d74b5d54c53893b841fb1424f3583af110
|
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
3
|
+
|
4
|
+
# Declare your gem's dependencies in countrizable.gemspec.
|
5
|
+
# Bundler will treat runtime dependencies like base dependencies, and
|
6
|
+
# development dependencies will be added by default to the :development group.
|
7
|
+
gemspec
|
8
|
+
|
9
|
+
# Declare any dependencies that are still in development here instead of in
|
10
|
+
# your gemspec. These might include edge Rails or gems from your path or
|
11
|
+
# Git. Remember to move these dependencies to your gemspec before releasing
|
12
|
+
# your gem to rubygems.org.
|
13
|
+
|
14
|
+
# To use a debugger
|
15
|
+
# gem 'byebug', group: [:development, :test]
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2018 zeopix
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# Countrizable
|
2
|
+
Short description and motivation.
|
3
|
+
|
4
|
+
## Usage
|
5
|
+
How to use my plugin.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'countrizable'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
```bash
|
16
|
+
$ bundle
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
```bash
|
21
|
+
$ gem install countrizable
|
22
|
+
```
|
23
|
+
|
24
|
+
## Contributing
|
25
|
+
Contribution directions go here.
|
26
|
+
|
27
|
+
## License
|
28
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Countrizable'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'bundler/gem_tasks'
|
18
|
+
|
19
|
+
require 'rake/testtask'
|
20
|
+
|
21
|
+
Rake::TestTask.new(:test) do |t|
|
22
|
+
t.libs << 'test'
|
23
|
+
t.pattern = 'test/**/*_test.rb'
|
24
|
+
t.verbose = false
|
25
|
+
end
|
26
|
+
|
27
|
+
task default: :test
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module Countrizable
|
2
|
+
module ActiveRecord
|
3
|
+
module ActMacro
|
4
|
+
def country_attribute(*attr_names)
|
5
|
+
options = attr_names.extract_options!
|
6
|
+
# Bypass setup_countries! if the initial bootstrapping is done already.
|
7
|
+
setup_countries!(options) unless country_attribute?
|
8
|
+
check_columns!(attr_names)
|
9
|
+
|
10
|
+
# Add any extra country attributes.
|
11
|
+
attr_names = attr_names.map(&:to_sym)
|
12
|
+
attr_names -= country_attribute_names if defined?(country_attribute_names)
|
13
|
+
|
14
|
+
allow_country_of_attributes(attr_names) if attr_names.present?
|
15
|
+
end
|
16
|
+
|
17
|
+
def class_name
|
18
|
+
@class_name ||= begin
|
19
|
+
class_name = table_name[table_name_prefix.length..-(table_name_suffix.length + 1)].downcase.camelize
|
20
|
+
pluralize_table_names ? class_name.singularize : class_name
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def country_attribute?
|
25
|
+
included_modules.include?(InstanceMethods)
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def allow_country_of_attributes(attr_names)
|
31
|
+
attr_names.each do |attr_name|
|
32
|
+
# Detect and apply serialization.
|
33
|
+
enable_serializable_attribute(attr_name)
|
34
|
+
|
35
|
+
# Create accessors for the attribute.
|
36
|
+
define_country_attr_accessor(attr_name)
|
37
|
+
define_country_values_accessor(attr_name)
|
38
|
+
|
39
|
+
# Add attribute to the list.
|
40
|
+
self.country_attribute_names << attr_name
|
41
|
+
end
|
42
|
+
|
43
|
+
begin
|
44
|
+
if ::ActiveRecord::VERSION::STRING > "5.0" && table_exists? && country_value_class.table_exists?
|
45
|
+
self.ignored_columns += country_attribute_names.map(&:to_s)
|
46
|
+
reset_column_information
|
47
|
+
end
|
48
|
+
rescue ::ActiveRecord::NoDatabaseError
|
49
|
+
warn 'Unable to connect to a database. Countrizable skipped ignoring columns of country attributes.'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def check_columns!(attr_names)
|
54
|
+
# If tables do not exist or Rails version is greater than 5, do not warn about conflicting columns
|
55
|
+
return unless ::ActiveRecord::VERSION::STRING < "5.0" && table_exists? && country_value_class.table_exists?
|
56
|
+
if (overlap = attr_names.map(&:to_s) & column_names).present?
|
57
|
+
ActiveSupport::Deprecation.warn(
|
58
|
+
["You have defined one or more country attributes with names that conflict with column(s) on the model table. ",
|
59
|
+
"Countrizable does not support this configuration anymore, remove or rename column(s) on the model table.\n",
|
60
|
+
"Model name (table name): #{model_name} (#{table_name})\n",
|
61
|
+
"Attribute name(s): #{overlap.join(', ')}\n"].join
|
62
|
+
)
|
63
|
+
end
|
64
|
+
rescue ::ActiveRecord::NoDatabaseError
|
65
|
+
warn 'Unable to connect to a database. Countrizable skipped checking attributes with conflicting column names.'
|
66
|
+
end
|
67
|
+
|
68
|
+
def apply_countrizable_options(options)
|
69
|
+
options[:table_name] ||= "#{table_name.singularize}_country_values"
|
70
|
+
options[:foreign_key] ||= class_name.foreign_key
|
71
|
+
|
72
|
+
class_attribute :country_attribute_names, :country_value_options, :fallbacks_for_empty_country_values
|
73
|
+
self.country_attribute_names = []
|
74
|
+
self.country_value_options = options
|
75
|
+
self.fallbacks_for_empty_country_values = options[:fallbacks_for_empty_country_values]
|
76
|
+
end
|
77
|
+
|
78
|
+
def enable_serializable_attribute(attr_name)
|
79
|
+
serializer = self.countrizable_serialized_attributes[attr_name]
|
80
|
+
if serializer.present?
|
81
|
+
if defined?(::ActiveRecord::Coders::YAMLColumn) &&
|
82
|
+
serializer.is_a?(::ActiveRecord::Coders::YAMLColumn)
|
83
|
+
serializer = serializer.object_class
|
84
|
+
end
|
85
|
+
|
86
|
+
country_value_class.send :serialize, attr_name, serializer
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def setup_countries!(options)
|
91
|
+
apply_countrizable_options(options)
|
92
|
+
|
93
|
+
include InstanceMethods
|
94
|
+
extend ClassMethods, Migration
|
95
|
+
|
96
|
+
country_value_class.table_name = options[:table_name]
|
97
|
+
|
98
|
+
has_many :country_values, :class_name => country_value_class.name,
|
99
|
+
:foreign_key => options[:foreign_key],
|
100
|
+
:dependent => :destroy,
|
101
|
+
:extend => HasManyExtensions,
|
102
|
+
:autosave => false,
|
103
|
+
:inverse_of => :countrizable_model
|
104
|
+
|
105
|
+
after_create :save_country_values!
|
106
|
+
after_update :save_country_values!
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
module HasManyExtensions
|
111
|
+
def find_or_initialize_by_country_code(country_code)
|
112
|
+
with_country_code(country_code.to_s).first || build(:country_code => country_code.to_s)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module Countrizable
|
2
|
+
module ActiveRecord
|
3
|
+
class Adapter
|
4
|
+
# The cache caches attributes that already were looked up for read access.
|
5
|
+
# The stash keeps track of new or changed values that need to be saved.
|
6
|
+
attr_accessor :record, :stash
|
7
|
+
private :record=, :stash=
|
8
|
+
|
9
|
+
delegate :country_value_class, :to => :'record.class'
|
10
|
+
|
11
|
+
def initialize(record)
|
12
|
+
@record = record
|
13
|
+
@stash = Attributes.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def fetch_stash(country_code, name)
|
17
|
+
stash.read(country_code, name)
|
18
|
+
end
|
19
|
+
|
20
|
+
delegate :contains?, :to => :stash, :prefix => :stash
|
21
|
+
delegate :write, :to => :stash
|
22
|
+
|
23
|
+
def fetch(country_code, name)
|
24
|
+
record.countrizable_fallbacks(country_code).each do |fallback|
|
25
|
+
value = stash.contains?(fallback, name) ? fetch_stash(fallback, name) : fetch_attribute(fallback, name)
|
26
|
+
|
27
|
+
unless fallbacks_for?(value)
|
28
|
+
set_metadata(value, :country_code => fallback, :requested_country_code => country_code)
|
29
|
+
return value
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
return nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def save_country_values!
|
37
|
+
stash.each do |country_code, attrs|
|
38
|
+
next if attrs.empty?
|
39
|
+
|
40
|
+
country_value = record.country_values_by_country_code[country_code] ||
|
41
|
+
record.country_values.build(country_code: country_code.to_s)
|
42
|
+
attrs.each do |name, value|
|
43
|
+
value = value.val if value.is_a?(Arel::Nodes::Casted)
|
44
|
+
country_value[name] = value
|
45
|
+
end
|
46
|
+
|
47
|
+
ensure_foreign_key_for(country_value)
|
48
|
+
country_value.save!
|
49
|
+
end
|
50
|
+
|
51
|
+
reset
|
52
|
+
end
|
53
|
+
|
54
|
+
def reset
|
55
|
+
stash.clear
|
56
|
+
end
|
57
|
+
|
58
|
+
protected
|
59
|
+
|
60
|
+
# Sometimes the country_values is initialised before a foreign key can be set.
|
61
|
+
def ensure_foreign_key_for(country_value)
|
62
|
+
# AR >= 4.1 reflections renamed to _reflections
|
63
|
+
country_value[country_value.class.reflections.stringify_keys["countrizable_model"].foreign_key] = record.id
|
64
|
+
end
|
65
|
+
|
66
|
+
def type_cast(name, value)
|
67
|
+
return value.presence unless column = column_for_attribute(name)
|
68
|
+
|
69
|
+
column.type_cast value
|
70
|
+
end
|
71
|
+
|
72
|
+
def column_for_attribute(name)
|
73
|
+
country_value_class.columns_hash[name.to_s]
|
74
|
+
end
|
75
|
+
|
76
|
+
def unserializable_attribute?(name, column)
|
77
|
+
column.text? && country_value_class.serialized_attributes[name.to_s]
|
78
|
+
end
|
79
|
+
|
80
|
+
def fetch_attribute(country_code, name)
|
81
|
+
country_value = record.country_value_for(country_code, false)
|
82
|
+
if country_value
|
83
|
+
country_value.send(name)
|
84
|
+
else
|
85
|
+
record.class.country_value_class.new.send(name)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def set_metadata(object, metadata)
|
90
|
+
object.country_value_metadata.merge!(metadata) if object.respond_to?(:country_value_metadata)
|
91
|
+
object
|
92
|
+
end
|
93
|
+
|
94
|
+
def country_value_metadata_accessor(object)
|
95
|
+
return if obj.respond_to?(:country_value_metadata)
|
96
|
+
class << object; attr_accessor :country_value_metadata end
|
97
|
+
object.country_value_metadata ||= {}
|
98
|
+
end
|
99
|
+
|
100
|
+
def fallbacks_for?(object)
|
101
|
+
object.nil? || (fallbacks_for_empty_country_values? && object.blank?)
|
102
|
+
end
|
103
|
+
|
104
|
+
delegate :fallbacks_for_empty_country_values?, :to => :record, :prefix => false
|
105
|
+
prepend AdapterDirty
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Countrizable
|
2
|
+
module ActiveRecord
|
3
|
+
module AdapterDirty
|
4
|
+
def write country_code, name, value
|
5
|
+
# Dirty tracking, paraphrased from
|
6
|
+
# ActiveRecord::AttributeMethods::Dirty#write_attribute.
|
7
|
+
name = name.to_s
|
8
|
+
store_old_value name, country_code
|
9
|
+
old_values = dirty[name]
|
10
|
+
old_value = old_values[country_code]
|
11
|
+
is_changed = record.send :attribute_changed?, name
|
12
|
+
if is_changed && value == old_value
|
13
|
+
# If there's already a change, delete it if this undoes the change.
|
14
|
+
old_values.delete country_code
|
15
|
+
if old_values.empty?
|
16
|
+
_reset_attribute name
|
17
|
+
end
|
18
|
+
elsif !is_changed
|
19
|
+
# If there's not a change yet, record it.
|
20
|
+
record.send(:attribute_will_change!, name) if old_value != value
|
21
|
+
end
|
22
|
+
|
23
|
+
super country_code, name, value
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_writer :dirty
|
27
|
+
def dirty
|
28
|
+
@dirty ||= {}
|
29
|
+
end
|
30
|
+
|
31
|
+
def store_old_value name, country_code
|
32
|
+
dirty[name] ||= {}
|
33
|
+
unless dirty[name].key? country_code
|
34
|
+
old = fetch(country_code, name)
|
35
|
+
old = old.dup if old.duplicable?
|
36
|
+
dirty[name][country_code] = old
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def clear_dirty
|
41
|
+
self.dirty = {}
|
42
|
+
end
|
43
|
+
|
44
|
+
def _reset_attribute name
|
45
|
+
record.send("#{name}=", record.changed_attributes[name])
|
46
|
+
record.send(:clear_attribute_changes, [name])
|
47
|
+
end
|
48
|
+
|
49
|
+
def reset
|
50
|
+
clear_dirty
|
51
|
+
super
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# Helper class for storing values per country_codes. Used by Countrizable::Adapter
|
2
|
+
# to stash and cache attribute values.
|
3
|
+
|
4
|
+
module Countrizable
|
5
|
+
module ActiveRecord
|
6
|
+
class Attributes < Hash # TODO: Think about using HashWithIndifferentAccess ?
|
7
|
+
def [](country_code)
|
8
|
+
country_code = country_code.to_sym
|
9
|
+
self[country_code] = {} unless has_key?(country_code)
|
10
|
+
self.fetch(country_code)
|
11
|
+
end
|
12
|
+
|
13
|
+
def contains?(country_code, name)
|
14
|
+
self[country_code].has_key?(name.to_s)
|
15
|
+
end
|
16
|
+
|
17
|
+
def read(country_code, name)
|
18
|
+
self[country_code][name.to_s]
|
19
|
+
end
|
20
|
+
|
21
|
+
def write(country_code, name, value)
|
22
|
+
self[country_code][name.to_s] = value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module Countrizable
|
2
|
+
module ActiveRecord
|
3
|
+
module ClassMethods
|
4
|
+
delegate :values_country_codes, :set_country_values_table_name, :to => :country_value_class
|
5
|
+
|
6
|
+
if ::ActiveRecord::VERSION::STRING < "5.0.0"
|
7
|
+
def columns_hash
|
8
|
+
super.except(*country_attribute_names.map(&:to_s))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def with_country_codes(*country_code)
|
13
|
+
all.merge country_value_class.with_country_codes(*country_codes)
|
14
|
+
end
|
15
|
+
|
16
|
+
def with_country_values(*country_codes)
|
17
|
+
country_codes = values_country_codes if country_codes.empty?
|
18
|
+
preload(:country_values).joins(:country_values).readonly(false).with_country_codes(country_codes).tap do |query|
|
19
|
+
query.distinct! unless country_codes.flatten.one?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def with_required_attributes
|
24
|
+
warn 'with_required_attributes is deprecated and will be removed in the next release of Countrizable.'
|
25
|
+
required_country_attributes.inject(all) do |scope, name|
|
26
|
+
scope.where("#{country_column_name(name)} IS NOT NULL")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def with_country_attribute(name, value, country = Countrizable.fallbacks)
|
31
|
+
with_country_values.where(
|
32
|
+
country_column_name(name) => value,
|
33
|
+
country_column_name(:country_code) => Array(country_codes).map(&:to_s)
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def country_attributed?(name)
|
38
|
+
country_attribute_names.include?(name.to_sym)
|
39
|
+
end
|
40
|
+
|
41
|
+
def required_attributes
|
42
|
+
warn 'required_attributes is deprecated and will be removed in the next release of Countrizable.'
|
43
|
+
validators.map { |v| v.attributes if v.is_a?(ActiveModel::Validations::PresenceValidator) }.flatten
|
44
|
+
end
|
45
|
+
|
46
|
+
def required_country_attributes
|
47
|
+
warn 'required_country_attributes is deprecated and will be removed in the next release of Countrizable.'
|
48
|
+
country_attribute_names & required_attributes
|
49
|
+
end
|
50
|
+
|
51
|
+
def country_value_class
|
52
|
+
@country_value_class ||= begin
|
53
|
+
if self.const_defined?(:CountryValue, false)
|
54
|
+
klass = self.const_get(:CountryValue, false)
|
55
|
+
else
|
56
|
+
klass = self.const_set(:CountryValue, Class.new(Countrizable::ActiveRecord::CountryValue))
|
57
|
+
end
|
58
|
+
|
59
|
+
klass.belongs_to :countrizable_model,
|
60
|
+
class_name: self.name,
|
61
|
+
foreign_key: country_value_options[:foreign_key],
|
62
|
+
inverse_of: :country_values,
|
63
|
+
touch: country_value_options.fetch(:touch, false)
|
64
|
+
klass
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def country_values_table_name
|
69
|
+
country_value_class.table_name
|
70
|
+
end
|
71
|
+
|
72
|
+
def country_column_name(name)
|
73
|
+
"#{country_value_class.table_name}.#{name}"
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
# Override the default relation method in order to return a subclass
|
79
|
+
# of ActiveRecord::Relation with custom finder and calculation methods
|
80
|
+
# for country attributes.
|
81
|
+
def relation
|
82
|
+
super.extending!(CountryAttributesQuery)
|
83
|
+
end
|
84
|
+
|
85
|
+
protected
|
86
|
+
|
87
|
+
def define_country_attr_reader(name)
|
88
|
+
define_method(name) do |*args|
|
89
|
+
Countrizable::Interpolation.interpolate(name, self, args)
|
90
|
+
end
|
91
|
+
alias_method :"#{name}_before_type_cast", name
|
92
|
+
end
|
93
|
+
|
94
|
+
def define_country_attr_writer(name)
|
95
|
+
define_method(:"#{name}=") do |value|
|
96
|
+
write_attribute(name, value)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def define_country_attr_accessor(name)
|
101
|
+
attribute(name, ::ActiveRecord::Type::Value.new)
|
102
|
+
define_country_attr_reader(name)
|
103
|
+
define_country_attr_writer(name)
|
104
|
+
end
|
105
|
+
|
106
|
+
def define_country_values_reader(name)
|
107
|
+
define_method(:"#{name}_country_values") do
|
108
|
+
hash = country_attribute_by_country_code(name)
|
109
|
+
countrizable.stash.keys.each_with_object(hash) do |country_code, result|
|
110
|
+
result[country_code] = countrizable.fetch_stash(country_code, name) if countrizable.stash_contains?(country_code, name)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def define_country_values_writer(name)
|
116
|
+
define_method(:"#{name}_country_values=") do |value|
|
117
|
+
value.each do |(country_code, _value)|
|
118
|
+
write_attribute name, _value, :country_code => country_code
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def define_country_values_accessor(name)
|
124
|
+
define_country_values_reader(name)
|
125
|
+
define_country_values_writer(name)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|