flatter 0.1.0 → 0.2.0
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 +4 -4
- data/README.md +679 -5
- data/flatter.gemspec +1 -0
- data/lib/flatter/mapper/attribute_methods.rb +72 -5
- data/lib/flatter/mapper/collection.rb +193 -0
- data/lib/flatter/mapper/factory.rb +24 -4
- data/lib/flatter/mapper/mapping.rb +2 -4
- data/lib/flatter/mapper/mounting.rb +37 -13
- data/lib/flatter/mapper/options.rb +1 -1
- data/lib/flatter/mapper/persistence.rb +11 -2
- data/lib/flatter/mapper/target.rb +53 -7
- data/lib/flatter/mapper/traits.rb +12 -23
- data/lib/flatter/mapper/write_with_indifferent_access.rb +8 -0
- data/lib/flatter/mapper.rb +6 -2
- data/lib/flatter/version.rb +1 -1
- metadata +18 -2
data/flatter.gemspec
CHANGED
@@ -29,6 +29,7 @@ Gem::Specification.new do |spec|
|
|
29
29
|
spec.add_development_dependency "bundler", "~> 1.10"
|
30
30
|
spec.add_development_dependency "rake", "~> 10.0"
|
31
31
|
spec.add_development_dependency "rspec"
|
32
|
+
spec.add_development_dependency "rspec-its"
|
32
33
|
spec.add_development_dependency "simplecov", ">= 0.9"
|
33
34
|
spec.add_development_dependency "pry"
|
34
35
|
spec.add_development_dependency "pry-nav"
|
@@ -1,7 +1,8 @@
|
|
1
1
|
module Flatter
|
2
2
|
module Mapper::AttributeMethods
|
3
3
|
def respond_to_missing?(name, *)
|
4
|
-
mapping_names.map{ |name| [name,
|
4
|
+
acceptable = mapping_names.map{ |name| [name, "#{name}="] }.flatten + mounting_names
|
5
|
+
acceptable.uniq.map(&:to_sym).include?(name) || super
|
5
6
|
end
|
6
7
|
|
7
8
|
def method_missing(name, *args, &block)
|
@@ -13,13 +14,79 @@ module Flatter
|
|
13
14
|
send(name, *args, &block)
|
14
15
|
end
|
15
16
|
|
17
|
+
def mounting(name)
|
18
|
+
find_mounting(name.to_s)
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_mounting(name)
|
22
|
+
local_mountings.each do |mounting|
|
23
|
+
if mounting.name == name || (mounting.pluralized? && mounting.name.pluralize == name)
|
24
|
+
return mounting
|
25
|
+
end
|
26
|
+
nested = mounting.find_mounting(name)
|
27
|
+
return nested if nested.present?
|
28
|
+
end
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
protected :find_mounting
|
32
|
+
|
33
|
+
def find_mounting_with(mapping_name)
|
34
|
+
mapping_name = mapping_name.to_s
|
35
|
+
|
36
|
+
match = local_mappings.any? do |mapping|
|
37
|
+
if collection? || pluralized?
|
38
|
+
mapping.name.pluralize == mapping_name
|
39
|
+
else
|
40
|
+
mapping.name == mapping_name
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
return self if match
|
45
|
+
|
46
|
+
local_mountings.each do |mounting|
|
47
|
+
nested = mounting.find_mounting_with(mapping_name)
|
48
|
+
return nested if nested.present?
|
49
|
+
end
|
50
|
+
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
protected :find_mounting_with
|
54
|
+
|
16
55
|
def attribute_methods
|
17
|
-
|
56
|
+
_mapping_names = mapping_names
|
57
|
+
_mounting_names = mounting_names - _mapping_names
|
58
|
+
|
18
59
|
Module.new do
|
19
|
-
|
20
|
-
define_method(name)
|
60
|
+
_mounting_names.each do |name|
|
61
|
+
define_method(name) do
|
62
|
+
mount = find_mounting(name)
|
63
|
+
if mount.collection?
|
64
|
+
mount.read[name.to_s]
|
65
|
+
elsif mount.pluralized?
|
66
|
+
Array(mountings[mount.name]).map(&:read)
|
67
|
+
else
|
68
|
+
mount.read
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
_mapping_names.each do |name|
|
74
|
+
define_method(name) do |*args|
|
75
|
+
mount = find_mounting_with(name)
|
76
|
+
if mount.collection? || mount.pluralized?
|
77
|
+
Array(mapping(name.singularize)).map{ |map| map.read(*args) }
|
78
|
+
else
|
79
|
+
mapping(name).read(*args)
|
80
|
+
end
|
81
|
+
end
|
21
82
|
|
22
|
-
define_method(
|
83
|
+
define_method("#{name}=") do |value|
|
84
|
+
mount = find_mounting_with(name)
|
85
|
+
if mount.collection? || mount.pluralized?
|
86
|
+
fail RuntimeError, "Cannot directly write to a collection"
|
87
|
+
end
|
88
|
+
mapping(name).write(value)
|
89
|
+
end
|
23
90
|
end
|
24
91
|
end
|
25
92
|
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
module Flatter
|
2
|
+
module Mapper::Collection
|
3
|
+
NonUniqKeysError = Class.new(RuntimeError)
|
4
|
+
|
5
|
+
def self.prepended(base)
|
6
|
+
base.send(:include, Concern)
|
7
|
+
end
|
8
|
+
|
9
|
+
module FactoryMethods
|
10
|
+
def create(*)
|
11
|
+
super.tap do |mapper|
|
12
|
+
mapper.options.merge!(collection: collection?)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def default_mapper_class_name
|
17
|
+
collection? ? "#{name.singularize.camelize}Mapper" : super
|
18
|
+
end
|
19
|
+
|
20
|
+
def collection?
|
21
|
+
options[:collection] == true ||
|
22
|
+
(options[:collection] != false && name == name.pluralize)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module Concern
|
27
|
+
extend ActiveSupport::Concern
|
28
|
+
|
29
|
+
included do
|
30
|
+
mapper_options.push(:collection, :item_index)
|
31
|
+
attr_accessor :item_index
|
32
|
+
end
|
33
|
+
|
34
|
+
module ClassMethods
|
35
|
+
def key(arg = nil)
|
36
|
+
args = []
|
37
|
+
options = {writer: false}
|
38
|
+
|
39
|
+
case arg
|
40
|
+
when String, Symbol
|
41
|
+
options[:key] = arg.to_sym
|
42
|
+
when Proc
|
43
|
+
args << :key
|
44
|
+
options[:reader] = arg
|
45
|
+
else
|
46
|
+
fail ArgumentError, "Cannot use '#{arg}' as collection key"
|
47
|
+
end
|
48
|
+
|
49
|
+
map *args, **options
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def remove_items(keys)
|
54
|
+
collection.reject! do |item|
|
55
|
+
(item[:key].nil? || keys.include?(item[:key])) &&
|
56
|
+
delete_target_item(item.target)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
private :remove_items
|
60
|
+
|
61
|
+
def delete_target_item(item)
|
62
|
+
!!target.delete(item)
|
63
|
+
end
|
64
|
+
|
65
|
+
def update_item(key, params)
|
66
|
+
collection.find{ |item| item[:key] == key }.write(params)
|
67
|
+
end
|
68
|
+
|
69
|
+
def add_item(params)
|
70
|
+
collection << clone.tap do |mapper|
|
71
|
+
item = target_class.new
|
72
|
+
mapper.reset_locals!
|
73
|
+
mapper.set_target!(item)
|
74
|
+
mapper.item_index = collection.length
|
75
|
+
mapper.write(params)
|
76
|
+
add_target_item(item)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def add_target_item(item)
|
81
|
+
target << item
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def read
|
86
|
+
return super unless collection?
|
87
|
+
|
88
|
+
values = collection.map(&:read)
|
89
|
+
|
90
|
+
assert_key_uniqueness!(values)
|
91
|
+
|
92
|
+
{name => values}
|
93
|
+
end
|
94
|
+
|
95
|
+
def write(params)
|
96
|
+
return super unless collection?
|
97
|
+
return unless params.key?(name)
|
98
|
+
|
99
|
+
data = params[name]
|
100
|
+
assert_collection!(data)
|
101
|
+
|
102
|
+
keys = collection.map(&:key)
|
103
|
+
remove_items(keys - data.map{ |p| p[:key] })
|
104
|
+
|
105
|
+
data.each do |params|
|
106
|
+
if params.key?(:key)
|
107
|
+
update_item(params[:key], params.except(:key))
|
108
|
+
else
|
109
|
+
add_item(params)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def pluralize!
|
115
|
+
@_pluralized = true
|
116
|
+
end
|
117
|
+
|
118
|
+
def pluralized?
|
119
|
+
!!@_pluralized
|
120
|
+
end
|
121
|
+
|
122
|
+
def mapping_names
|
123
|
+
super.map{ |name| collection? || pluralized? ? name.pluralize : name }
|
124
|
+
end
|
125
|
+
|
126
|
+
def mounting_names
|
127
|
+
super.map{ |name| pluralized? ? name.pluralize : name }
|
128
|
+
end
|
129
|
+
|
130
|
+
def local_mountings
|
131
|
+
super.each{ |mapper| mapper.pluralize! if collection? || pluralized? }
|
132
|
+
end
|
133
|
+
protected :local_mountings
|
134
|
+
|
135
|
+
def assert_key_uniqueness!(values)
|
136
|
+
keys = values.map{ |v| v['key'] }.compact
|
137
|
+
keys == keys.uniq or
|
138
|
+
fail NonUniqKeysError, "All keys in collection '#{name}' should be uniq, but were not"
|
139
|
+
end
|
140
|
+
private :assert_key_uniqueness!
|
141
|
+
|
142
|
+
def assert_collection!(data)
|
143
|
+
unless data.respond_to?(:each)
|
144
|
+
fail ArgumentError, "Cannot write to '#{name}': argument is not a collection"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
private :assert_collection!
|
148
|
+
|
149
|
+
def collection
|
150
|
+
return nil unless collection?
|
151
|
+
|
152
|
+
@collection ||= target.each.with_index.map do |item, index|
|
153
|
+
clone.tap do |mapper|
|
154
|
+
mapper.reset_locals!
|
155
|
+
mapper.set_target! item
|
156
|
+
mapper.item_index = index
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def prefix
|
162
|
+
return super if mounter.nil?
|
163
|
+
|
164
|
+
[mounter.prefix, item_name].compact.join(?.).presence
|
165
|
+
end
|
166
|
+
protected :prefix
|
167
|
+
|
168
|
+
def item_name
|
169
|
+
"#{name}.#{item_index}" if item_index.present?
|
170
|
+
end
|
171
|
+
protected :item_name
|
172
|
+
|
173
|
+
def as_inner_mountings
|
174
|
+
if collection?
|
175
|
+
ensure_target!
|
176
|
+
collection.map{ |item| item.as_inner_mountings }
|
177
|
+
else
|
178
|
+
super
|
179
|
+
end
|
180
|
+
end
|
181
|
+
protected :as_inner_mountings
|
182
|
+
|
183
|
+
def reset_locals!
|
184
|
+
@_local_mappings = nil
|
185
|
+
@_local_mountings = nil
|
186
|
+
end
|
187
|
+
protected :reset_locals!
|
188
|
+
|
189
|
+
def collection?
|
190
|
+
options[:collection] && item_index.nil?
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
@@ -4,6 +4,9 @@ module Flatter
|
|
4
4
|
prepend Flatter::Mapper::Mounting::FactoryMethods
|
5
5
|
prepend Flatter::Mapper::Traits::FactoryMethods
|
6
6
|
prepend Flatter::Mapper::Options::FactoryMethods
|
7
|
+
prepend Flatter::Mapper::Collection::FactoryMethods
|
8
|
+
|
9
|
+
NoTargetError = Class.new(RuntimeError)
|
7
10
|
|
8
11
|
attr_reader :name, :options
|
9
12
|
|
@@ -16,19 +19,36 @@ module Flatter
|
|
16
19
|
end
|
17
20
|
|
18
21
|
def mapper_class_name
|
19
|
-
options[:mapper_class_name] ||
|
22
|
+
options[:mapper_class_name] || modulize(default_mapper_class_name)
|
23
|
+
end
|
24
|
+
|
25
|
+
def default_mapper_class_name
|
26
|
+
"#{name.camelize}Mapper"
|
27
|
+
end
|
28
|
+
|
29
|
+
def create(*)
|
30
|
+
mapper_class.new.tap{ |mapper| mapper.factory = self }
|
20
31
|
end
|
21
32
|
|
22
|
-
def
|
23
|
-
|
33
|
+
def modulize(class_name)
|
34
|
+
if i = options[:mounter_name].rindex('::')
|
35
|
+
"#{options[:mounter_name][0...i]}::#{class_name}"
|
36
|
+
else
|
37
|
+
class_name
|
38
|
+
end
|
24
39
|
end
|
40
|
+
private :modulize
|
25
41
|
|
26
42
|
def fetch_target_from(mapper)
|
27
43
|
default_target_from(mapper)
|
28
44
|
end
|
29
45
|
|
30
46
|
def default_target_from(mapper)
|
31
|
-
|
47
|
+
if mapper.target.respond_to?(name)
|
48
|
+
mapper.target.public_send(name)
|
49
|
+
else
|
50
|
+
fail NoTargetError, "Unable to implicitly fetch target for '#{name}' from #{mapper}"
|
51
|
+
end
|
32
52
|
end
|
33
53
|
private :default_target_from
|
34
54
|
end
|
@@ -37,15 +37,13 @@ module Flatter
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def write(params)
|
40
|
-
params = params.with_indifferent_access
|
41
40
|
local_mappings.each{ |mapping| mapping.write_from_params(params) }
|
42
|
-
|
43
|
-
params
|
44
41
|
end
|
45
42
|
|
46
43
|
def local_mappings
|
47
44
|
@_local_mappings ||= self.class.mappings.values.map{ |factory| factory.create(self) }
|
48
45
|
end
|
46
|
+
protected :local_mappings
|
49
47
|
|
50
48
|
def mappings
|
51
49
|
local_mappings.each_with_object({}) do |mapping, res|
|
@@ -54,7 +52,7 @@ module Flatter
|
|
54
52
|
end
|
55
53
|
|
56
54
|
def mapping_names
|
57
|
-
|
55
|
+
local_mappings.map(&:name)
|
58
56
|
end
|
59
57
|
|
60
58
|
def writable_mapping_names
|
@@ -11,9 +11,14 @@ module Flatter
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
+
included do
|
15
|
+
class_attribute :label
|
16
|
+
end
|
17
|
+
|
14
18
|
module ClassMethods
|
15
|
-
def mount(name,
|
16
|
-
|
19
|
+
def mount(name, **opts)
|
20
|
+
factory_options = opts.reverse_merge(mounter_name: self.name || label)
|
21
|
+
mountings[name.to_s] = Flatter::Mapper::Factory.new(name, **factory_options)
|
17
22
|
end
|
18
23
|
|
19
24
|
def mountings
|
@@ -35,26 +40,30 @@ module Flatter
|
|
35
40
|
super.tap do |mappings|
|
36
41
|
inner_mountings.each do |mounting|
|
37
42
|
mounting.local_mappings.each do |mapping|
|
38
|
-
mappings
|
43
|
+
mappings.merge!(mapping.name => mapping, &merging_proc)
|
39
44
|
end
|
40
45
|
end
|
41
46
|
end
|
42
47
|
end
|
43
48
|
|
49
|
+
def mapping_names
|
50
|
+
super + local_mountings.map(&:mapping_names).flatten
|
51
|
+
end
|
52
|
+
|
44
53
|
def read
|
45
|
-
|
54
|
+
local_mountings.map(&:read).inject(super, :merge)
|
46
55
|
end
|
47
56
|
|
48
57
|
def write(params)
|
49
|
-
super
|
50
|
-
|
51
|
-
|
58
|
+
super
|
59
|
+
local_mountings.each{ |mapper| mapper.write(params) }
|
60
|
+
@_inner_mountings = nil
|
52
61
|
end
|
53
62
|
|
54
63
|
def local_mountings
|
55
64
|
class_mountings_for(self.class)
|
56
65
|
end
|
57
|
-
|
66
|
+
protected :local_mountings
|
58
67
|
|
59
68
|
def class_mountings_for(klass)
|
60
69
|
class_mountings(klass).map{ |factory| factory.create(self) }
|
@@ -67,18 +76,33 @@ module Flatter
|
|
67
76
|
private :class_mountings
|
68
77
|
|
69
78
|
def mountings
|
70
|
-
@mountings ||= inner_mountings.
|
71
|
-
res
|
79
|
+
@mountings ||= inner_mountings.inject({}) do |res, mapper|
|
80
|
+
res.merge(mapper.full_name => mapper, &merging_proc)
|
72
81
|
end
|
73
82
|
end
|
74
83
|
|
75
|
-
def
|
76
|
-
|
84
|
+
def mounting_names
|
85
|
+
local_mounting_names + local_mountings.map(&:mounting_names).flatten
|
77
86
|
end
|
78
87
|
|
88
|
+
def local_mounting_names
|
89
|
+
local_mountings.map(&:name)
|
90
|
+
end
|
91
|
+
private :local_mounting_names
|
92
|
+
|
79
93
|
def inner_mountings
|
80
|
-
@_inner_mountings ||= local_mountings.map{ |mount|
|
94
|
+
@_inner_mountings ||= local_mountings.map{ |mount| mount.as_inner_mountings }.flatten
|
81
95
|
end
|
82
96
|
protected :inner_mountings
|
97
|
+
|
98
|
+
def as_inner_mountings
|
99
|
+
[self, inner_mountings]
|
100
|
+
end
|
101
|
+
protected :as_inner_mountings
|
102
|
+
|
103
|
+
def merging_proc
|
104
|
+
proc { |_, old, new| Array(old).push(new) }
|
105
|
+
end
|
106
|
+
private :merging_proc
|
83
107
|
end
|
84
108
|
end
|
@@ -61,12 +61,21 @@ module Flatter
|
|
61
61
|
private :self_mountings
|
62
62
|
|
63
63
|
def consolidate_errors!
|
64
|
-
root_mountings.
|
65
|
-
|
64
|
+
root_mountings.each do |mounting|
|
65
|
+
prefix = mounting.prefix
|
66
|
+
mounting.errors.to_hash.each do |name, errs|
|
67
|
+
error_key = [prefix, name].compact.join('.')
|
68
|
+
errors.messages.merge!(error_key.to_sym => errs){ |key, old, new| old + new }
|
69
|
+
end
|
66
70
|
end
|
67
71
|
end
|
68
72
|
private :consolidate_errors!
|
69
73
|
|
74
|
+
def prefix
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
protected :prefix
|
78
|
+
|
70
79
|
def errors
|
71
80
|
trait? ? mounter.errors : super
|
72
81
|
end
|
@@ -1,7 +1,13 @@
|
|
1
1
|
module Flatter
|
2
2
|
module Mapper::Target
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
3
5
|
NoTargetError = Class.new(ArgumentError)
|
4
6
|
|
7
|
+
included do
|
8
|
+
mapper_options << :target_class_name
|
9
|
+
end
|
10
|
+
|
5
11
|
module FactoryMethods
|
6
12
|
def fetch_target_from(mapper)
|
7
13
|
return super unless options.key?(:target)
|
@@ -19,20 +25,60 @@ module Flatter
|
|
19
25
|
end
|
20
26
|
end
|
21
27
|
|
22
|
-
|
23
|
-
|
24
|
-
def initialize(target, *)
|
25
|
-
unless target.present?
|
26
|
-
fail NoTargetError, "Target object is required to initialize #{self.class.name}"
|
27
|
-
end
|
28
|
+
attr_accessor :factory
|
28
29
|
|
30
|
+
def initialize(target = nil, *)
|
29
31
|
super
|
32
|
+
set_target!(target) if target.present?
|
33
|
+
end
|
30
34
|
|
31
|
-
|
35
|
+
def target
|
36
|
+
ensure_target!
|
37
|
+
@target
|
38
|
+
end
|
39
|
+
|
40
|
+
def ensure_target!
|
41
|
+
initialize_target unless target_initialized?
|
42
|
+
end
|
43
|
+
protected :ensure_target!
|
44
|
+
|
45
|
+
def initialize_target
|
46
|
+
return set_target!(mounter.target) if trait?
|
47
|
+
|
48
|
+
_mounter = mounter.trait? ? mounter.mounter : mounter
|
49
|
+
set_target!(factory.fetch_target_from(_mounter))
|
32
50
|
end
|
51
|
+
private :initialize_target
|
33
52
|
|
34
53
|
def set_target(target)
|
54
|
+
if trait?
|
55
|
+
mounter.set_target!(target)
|
56
|
+
else
|
57
|
+
set_target!(target)
|
58
|
+
trait_mountings.each{ |trait| trait.set_target!(target) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def set_target!(target)
|
63
|
+
fail NoTargetError, "Cannot set nil target for #{self.class.name}" if target.nil?
|
64
|
+
@_target_initialized = true
|
35
65
|
@target = target
|
36
66
|
end
|
67
|
+
|
68
|
+
def target_initialized?
|
69
|
+
!!@_target_initialized
|
70
|
+
end
|
71
|
+
|
72
|
+
def target_class
|
73
|
+
target_class_name.constantize
|
74
|
+
end
|
75
|
+
|
76
|
+
def target_class_name
|
77
|
+
options[:target_class_name] || default_target_class_name
|
78
|
+
end
|
79
|
+
|
80
|
+
def default_target_class_name
|
81
|
+
self.class.name.sub 'Mapper', ''
|
82
|
+
end
|
37
83
|
end
|
38
84
|
end
|
@@ -20,10 +20,6 @@ module Flatter
|
|
20
20
|
mounting.extend_with(extension) if extension.present?
|
21
21
|
end
|
22
22
|
end
|
23
|
-
|
24
|
-
def fetch_target_from(mapper)
|
25
|
-
trait? ? mapper.target : super
|
26
|
-
end
|
27
23
|
end
|
28
24
|
|
29
25
|
module ClassMethods
|
@@ -31,9 +27,11 @@ module Flatter
|
|
31
27
|
super.tap{ |f| f.extension = block }
|
32
28
|
end
|
33
29
|
|
34
|
-
def trait(name, &block)
|
30
|
+
def trait(name, label = nil, &block)
|
35
31
|
trait_name = "#{name}_trait"
|
36
|
-
mapper_class = Class.new(Flatter::Mapper
|
32
|
+
mapper_class = Class.new(Flatter::Mapper)
|
33
|
+
mapper_class.label = self.name || label
|
34
|
+
mapper_class.class_eval(&block)
|
37
35
|
|
38
36
|
if self.name.present?
|
39
37
|
mapper_class_name = trait_name.camelize
|
@@ -46,7 +44,7 @@ module Flatter
|
|
46
44
|
end
|
47
45
|
end
|
48
46
|
|
49
|
-
def initialize(
|
47
|
+
def initialize(_, *traits, **, &block)
|
50
48
|
super
|
51
49
|
|
52
50
|
set_traits(traits)
|
@@ -54,22 +52,8 @@ module Flatter
|
|
54
52
|
end
|
55
53
|
|
56
54
|
def extend_with(extension)
|
57
|
-
singleton_class.trait :extension, &extension
|
58
|
-
end
|
59
|
-
|
60
|
-
def set_target(target)
|
61
|
-
if trait?
|
62
|
-
mounter.set_target(target)
|
63
|
-
else
|
64
|
-
super
|
65
|
-
trait_mountings.each{ |trait| trait.set_target!(target) }
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def set_target!(target)
|
70
|
-
@target = target
|
55
|
+
singleton_class.trait :extension, self.class.name, &extension
|
71
56
|
end
|
72
|
-
protected :set_target!
|
73
57
|
|
74
58
|
def full_name
|
75
59
|
if name == 'extension_trait'
|
@@ -80,7 +64,7 @@ module Flatter
|
|
80
64
|
end
|
81
65
|
|
82
66
|
def local_mountings
|
83
|
-
@
|
67
|
+
@_local_mountings ||= class_mountings_for(singleton_class) + super
|
84
68
|
end
|
85
69
|
private :local_mountings
|
86
70
|
|
@@ -126,6 +110,11 @@ module Flatter
|
|
126
110
|
@trait = true
|
127
111
|
end
|
128
112
|
|
113
|
+
def local_mounting_names
|
114
|
+
super.reject{ |name| trait_mountings.any?{ |mount| mount.name == name } }
|
115
|
+
end
|
116
|
+
private :local_mounting_names
|
117
|
+
|
129
118
|
def trait_mountings
|
130
119
|
@_trait_mountings ||= local_mountings.select(&:trait?)
|
131
120
|
end
|
data/lib/flatter/mapper.rb
CHANGED
@@ -4,23 +4,27 @@ module Flatter
|
|
4
4
|
|
5
5
|
autoload :Factory
|
6
6
|
autoload :Options
|
7
|
-
autoload :Target
|
8
7
|
autoload :Mapping
|
9
8
|
autoload :Mounting
|
10
9
|
autoload :Traits
|
10
|
+
autoload :Target
|
11
11
|
autoload :AttributeMethods
|
12
12
|
autoload :Persistence
|
13
13
|
autoload :ModelName
|
14
|
+
autoload :Collection
|
15
|
+
autoload :WriteWithIndifferentAccess
|
14
16
|
|
15
17
|
include Options
|
16
|
-
include Target
|
17
18
|
include Mapping
|
18
19
|
include Mounting
|
19
20
|
include Traits
|
21
|
+
include Target
|
20
22
|
include AttributeMethods
|
21
23
|
include ActiveModel::Validations
|
22
24
|
include Persistence
|
23
25
|
prepend ModelName
|
26
|
+
prepend Collection
|
27
|
+
prepend WriteWithIndifferentAccess
|
24
28
|
|
25
29
|
def self.inherited(subclass)
|
26
30
|
subclass.mappings = mappings.dup
|
data/lib/flatter/version.rb
CHANGED