flatter 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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