nested 0.0.25 → 0.0.26
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/Gemfile +1 -0
- data/lib/nested/app.rb +31 -0
- data/lib/nested/integration/angular.rb +119 -0
- data/lib/nested/js.rb +42 -0
- data/lib/nested/many.rb +19 -0
- data/lib/nested/one.rb +29 -0
- data/lib/nested/redirect.rb +8 -0
- data/lib/nested/resource.rb +268 -0
- data/lib/nested/serializer.rb +45 -0
- data/lib/nested/serializer_field.rb +9 -0
- data/lib/nested/singleton.rb +13 -0
- data/lib/nested/with_many.rb +11 -0
- data/lib/nested/with_singleton.rb +11 -0
- data/lib/nested.rb +16 -566
- data/nested.gemspec +1 -1
- data/test/nested_test.rb +73 -109
- data/test/test_helper.rb +2 -2
- metadata +14 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f3204fac8586400602f52773cfa5cd557acaaf15
|
4
|
+
data.tar.gz: 4e017941f4c4bbc7ae02ee77df82a9b2944d26fd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 781858c122e8512d79e0fde3f0753ff97e473636c38e3bd5f4c5146a05b5954d0874ffb2a7980fbc04b944653b751026bc6506179d4732b2833d5b8325951b8f
|
7
|
+
data.tar.gz: db049be70b1b9899c2a08301c34534f6bdca917f470fe3af50bf3d6f9a73b0851c473618e8d54f38a296838c9353c3cbba6a960c84c64ec31b6f7574ded308ac
|
data/Gemfile
CHANGED
data/lib/nested/app.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Nested
|
2
|
+
class App
|
3
|
+
def self.inherited(clazz)
|
4
|
+
(class << clazz; self; end).instance_eval do
|
5
|
+
attr_accessor :sinatra
|
6
|
+
end
|
7
|
+
clazz.sinatra = Class.new(Sinatra::Base)
|
8
|
+
clazz.instance_variable_set("@config", {})
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.child_resource(name, clazz, resource_if_block, init_block, &block)
|
12
|
+
clazz.new(sinatra, name, nil, resource_if_block, init_block)
|
13
|
+
.tap{|r| r.instance_eval(&(block||Proc.new{ }))}
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.before(&block)
|
17
|
+
sinatra.before(&block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.after(&block)
|
21
|
+
sinatra.after(&block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.config(opts={})
|
25
|
+
@config.tap{|c| c.merge!(opts)}
|
26
|
+
end
|
27
|
+
|
28
|
+
extend WithSingleton
|
29
|
+
extend WithMany
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module Nested
|
2
|
+
module Integration
|
3
|
+
module Angular
|
4
|
+
def self.extended(clazz)
|
5
|
+
(class << clazz; self; end).instance_eval do
|
6
|
+
attr_accessor :angular_config
|
7
|
+
end
|
8
|
+
clazz.angular_config = {}
|
9
|
+
|
10
|
+
def clazz.angular(opts={})
|
11
|
+
@angular_config.merge!(opts)
|
12
|
+
end
|
13
|
+
|
14
|
+
def child_resource(*args, &block)
|
15
|
+
Helper::angularize(self, super)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module Helper
|
20
|
+
def self.angular_add_functions(app, js, resource)
|
21
|
+
resource.actions.each do |e|
|
22
|
+
method, action, block = e.values_at :method, :action, :block
|
23
|
+
block_args = block.parameters.map(&:last)
|
24
|
+
|
25
|
+
fun_name = Nested::Js::generate_function_name(resource, method, action)
|
26
|
+
|
27
|
+
args = Nested::Js::function_arguments(resource)
|
28
|
+
|
29
|
+
route_args = args.inject({}) do |memo, e|
|
30
|
+
idx = args.index(e)
|
31
|
+
memo[:"#{e}_id"] = "'+(typeof(values[#{idx}]) == 'number' ? values[#{idx}].toString() : (values[#{idx}].id || values[#{idx}]))+'"
|
32
|
+
memo
|
33
|
+
end
|
34
|
+
route = "#{app.config[:prefix]}" + resource.route_replace(resource.route(action), route_args)
|
35
|
+
when_args = args.map{|a| "$q.when(#{a})"}
|
36
|
+
|
37
|
+
js << " impl.#{fun_name}Url = function(#{args.join(',')}){"
|
38
|
+
js << " var deferred = $q.defer()"
|
39
|
+
js << " $q.all([#{when_args.join(',')}]).then(function(values){"
|
40
|
+
js << " deferred.resolve('#{route}')"
|
41
|
+
js << " })"
|
42
|
+
js << " return deferred.promise"
|
43
|
+
js << " }"
|
44
|
+
|
45
|
+
if [:get, :delete].include?(method)
|
46
|
+
args << "data" if !block_args.empty?
|
47
|
+
|
48
|
+
js << " impl.#{fun_name} = function(#{args.join(',')}){"
|
49
|
+
js << " var deferred = $q.defer()"
|
50
|
+
js << " $q.all([#{when_args.join(',')}]).then(function(values){"
|
51
|
+
js << " $http({"
|
52
|
+
js << " method: '#{method}', "
|
53
|
+
js << (" url: '#{route}'" + (block_args.empty? ? "" : ","))
|
54
|
+
js << " params: data" unless block_args.empty?
|
55
|
+
js << " })"
|
56
|
+
js << " .success(function(responseData){"
|
57
|
+
js << " deferred[responseData.ok ? 'resolve' : 'reject'](responseData.data)"
|
58
|
+
js << " })"
|
59
|
+
js << " .error(function(){ deferred.reject() })"
|
60
|
+
js << " });"
|
61
|
+
js << " return deferred.promise"
|
62
|
+
js << " }"
|
63
|
+
elsif method == :post
|
64
|
+
js << " impl.#{fun_name} = function(#{(args+['data']).join(',')}){"
|
65
|
+
js << " var deferred = $q.defer()"
|
66
|
+
js << " $q.all([#{when_args.join(',')}]).then(function(values){"
|
67
|
+
js << " $http({method: '#{method}', url: '#{route}', data: data})"
|
68
|
+
js << " .success(function(responseData){"
|
69
|
+
js << " deferred[responseData.ok ? 'resolve' : 'reject'](responseData.data)"
|
70
|
+
js << " })"
|
71
|
+
js << " .error(function(){ deferred.reject() })"
|
72
|
+
js << " });"
|
73
|
+
js << " return deferred.promise"
|
74
|
+
js << " }"
|
75
|
+
elsif method == :put
|
76
|
+
args << "data" if args.empty? || !block_args.empty?
|
77
|
+
|
78
|
+
js << " impl.#{fun_name} = function(#{args.join(',')}){"
|
79
|
+
js << " var deferred = $q.defer()"
|
80
|
+
js << " $q.all([#{when_args.join(',')}]).then(function(values){"
|
81
|
+
js << " $http({method: '#{method}', url: '#{route}', data: #{args.last}})"
|
82
|
+
js << " .success(function(responseData){"
|
83
|
+
js << " deferred[responseData.ok ? 'resolve' : 'reject'](responseData.data)"
|
84
|
+
js << " })"
|
85
|
+
js << " .error(function(){ deferred.reject() })"
|
86
|
+
js << " });"
|
87
|
+
js << " return deferred.promise"
|
88
|
+
js << " }"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
resource.resources.each {|r| angular_add_functions(app, js, r) }
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.angularize(app, resource)
|
96
|
+
js = []
|
97
|
+
|
98
|
+
module_name = "nested_#{resource.name}".camelcase(:lower)
|
99
|
+
|
100
|
+
js << "angular.module('#{module_name}#{app.angular_config[:service_suffix]}', [])"
|
101
|
+
js << ".factory('#{resource.name.to_s.camelcase.capitalize}#{app.angular_config[:service_suffix]}', function($http, $q){"
|
102
|
+
|
103
|
+
js << " var impl = {}"
|
104
|
+
angular_add_functions(app, js, resource)
|
105
|
+
js << " return impl"
|
106
|
+
|
107
|
+
js << "})"
|
108
|
+
|
109
|
+
response_transform = app.angular_config[:response_transform] || ->(code){ code }
|
110
|
+
|
111
|
+
app.sinatra.get "/#{resource.name}.js" do
|
112
|
+
content_type :js
|
113
|
+
instance_exec(js.join("\n"), &response_transform)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
data/lib/nested/js.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
module Nested
|
2
|
+
module Js
|
3
|
+
def self.generate_function_name(resource, method, action)
|
4
|
+
arr = []
|
5
|
+
|
6
|
+
arr << "update" if method == :put
|
7
|
+
arr << "create" if method == :post
|
8
|
+
arr << "destroy" if method == :delete
|
9
|
+
|
10
|
+
all = resource.self_and_parents.reverse
|
11
|
+
|
12
|
+
all.each do |e|
|
13
|
+
if e.is_a?(Many)
|
14
|
+
if e == all.last
|
15
|
+
if method == :post
|
16
|
+
arr << e.name.to_s.singularize.to_sym
|
17
|
+
else
|
18
|
+
arr << e.name
|
19
|
+
end
|
20
|
+
else
|
21
|
+
arr << e.name unless all[all.index(e) + 1].is_a?(One)
|
22
|
+
end
|
23
|
+
else
|
24
|
+
arr << e.name
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
arr << action if action
|
29
|
+
|
30
|
+
arr.map(&:to_s).join("_").camelcase(:lower)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.function_arguments(resource)
|
34
|
+
resource
|
35
|
+
.self_and_parents.select{|r| r.is_a?(One)}
|
36
|
+
.map{|r| r.name || r.parent.name}
|
37
|
+
.map(&:to_s)
|
38
|
+
.map(&:singularize)
|
39
|
+
.reverse
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/nested/many.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Nested
|
2
|
+
class Many < Resource
|
3
|
+
def one(&block)
|
4
|
+
one_if(PROC_TRUE, &block)
|
5
|
+
end
|
6
|
+
|
7
|
+
def one_if(resource_if_block, &block)
|
8
|
+
child_resource(self.name.to_s.singularize.to_sym, One, resource_if_block, nil, &block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def default_init_block
|
12
|
+
if parent
|
13
|
+
Proc.new{ instance_variable_get("@#{@__resource.parent.instance_variable_name}").send(@__resource.name) }
|
14
|
+
else
|
15
|
+
Proc.new { nil }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/nested/one.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Nested
|
2
|
+
class One < Resource
|
3
|
+
include WithMany
|
4
|
+
|
5
|
+
def initialize_serializer_factory
|
6
|
+
Serializer.new(parent.serializer.includes)
|
7
|
+
end
|
8
|
+
|
9
|
+
def default_init_block
|
10
|
+
if parent
|
11
|
+
Proc.new do
|
12
|
+
instance_variable_get("@#{@__resource.parent.instance_variable_name}")
|
13
|
+
.where(id: params[:"#{@__resource.parent.name.to_s.singularize.to_sym}_id"])
|
14
|
+
.first
|
15
|
+
end
|
16
|
+
else
|
17
|
+
Proc.new { nil }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_route_part
|
22
|
+
"/:#{@name}_id"
|
23
|
+
end
|
24
|
+
|
25
|
+
def instance_variable_name
|
26
|
+
parent.name.to_s.singularize.to_sym
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,268 @@
|
|
1
|
+
module Nested
|
2
|
+
class Resource
|
3
|
+
attr_reader :name, :parent, :actions, :resources, :serializer, :init_block, :sinatra
|
4
|
+
|
5
|
+
include WithSingleton
|
6
|
+
|
7
|
+
def initialize(sinatra, name, parent, resource_if_block, init_block)
|
8
|
+
raise "resource must be given a name" unless name
|
9
|
+
|
10
|
+
@sinatra = sinatra
|
11
|
+
@name = name
|
12
|
+
@parent = parent
|
13
|
+
@resources = []
|
14
|
+
@actions = []
|
15
|
+
|
16
|
+
raise "resource_if_block is nil, pass Nested::PROC_TRUE instead" unless resource_if_block
|
17
|
+
@resource_if_block = resource_if_block
|
18
|
+
|
19
|
+
unless @init_block = init_block
|
20
|
+
if is_a?(One)
|
21
|
+
@init_block = default_init_block
|
22
|
+
else
|
23
|
+
@init_block = resource_if_block != PROC_TRUE ? parent.try(:init_block) : default_init_block
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
raise "no init_block passed and could not lookup any parent or default init_block" unless @init_block
|
28
|
+
|
29
|
+
@before_blocks = []
|
30
|
+
@after_blocks = []
|
31
|
+
|
32
|
+
@serializer = initialize_serializer_factory
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize_serializer_factory
|
36
|
+
Serializer.new([])
|
37
|
+
end
|
38
|
+
|
39
|
+
def child_resource(name, clazz, resource_if_block, init_block, &block)
|
40
|
+
clazz.new(@sinatra, name, self, resource_if_block, init_block)
|
41
|
+
.tap{|r| r.instance_eval(&(block||Proc.new{ }))}
|
42
|
+
.tap{|r| @resources << r}
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_route_part
|
46
|
+
"/#{@name}"
|
47
|
+
end
|
48
|
+
|
49
|
+
def before(&block)
|
50
|
+
@before_blocks << block
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def after(&block)
|
55
|
+
@after_blocks << block
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def serialize(*args)
|
60
|
+
args.each {|arg| serializer + arg }
|
61
|
+
serializer
|
62
|
+
end
|
63
|
+
|
64
|
+
def serialize_include_if(condition, *args)
|
65
|
+
args.each {|arg| @serializer + SerializerField.new(arg, condition) }
|
66
|
+
end
|
67
|
+
|
68
|
+
def serialize_exclude_if(condition, *args)
|
69
|
+
args.each {|arg| @serializer - SerializerField.new(arg, condition) }
|
70
|
+
end
|
71
|
+
|
72
|
+
def route_replace(route, args)
|
73
|
+
args.each do |k, v|
|
74
|
+
route = route.gsub(":#{k}", "#{v}")
|
75
|
+
end
|
76
|
+
route
|
77
|
+
end
|
78
|
+
|
79
|
+
def route(action=nil)
|
80
|
+
"".tap do |r|
|
81
|
+
r << @parent.route if @parent
|
82
|
+
r << to_route_part
|
83
|
+
r << "/#{action}" if action
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
[:get, :post, :put, :delete].each do |method|
|
88
|
+
instance_eval do
|
89
|
+
define_method method do |action=nil, &block|
|
90
|
+
send(:"#{method}_if", PROC_TRUE, action, &block)
|
91
|
+
end
|
92
|
+
|
93
|
+
define_method :"#{method}_if" do |method_if_block, action=nil, &block|
|
94
|
+
create_sinatra_route method, action, method_if_block, &(block||proc {})
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def instance_variable_name
|
100
|
+
@name
|
101
|
+
end
|
102
|
+
|
103
|
+
def parents
|
104
|
+
(@parent ? @parent.parents + [@parent] : [])
|
105
|
+
end
|
106
|
+
|
107
|
+
def self_and_parents
|
108
|
+
(self.parents + [self]).reverse
|
109
|
+
end
|
110
|
+
|
111
|
+
# --------------------------
|
112
|
+
|
113
|
+
def sinatra_set_instance_variable(sinatra, name, value)
|
114
|
+
raise "variable @#{name} already defined" if sinatra.instance_variable_defined?(:"@#{name}")
|
115
|
+
sinatra.instance_variable_set(:"@#{name}", value)
|
116
|
+
end
|
117
|
+
|
118
|
+
def sinatra_init(sinatra)
|
119
|
+
sinatra.instance_variable_set("@__resource", self)
|
120
|
+
|
121
|
+
raise "resource_if is false for resource: #{self.name} " unless sinatra.instance_exec(&@resource_if_block)
|
122
|
+
|
123
|
+
@before_blocks.each{|e| sinatra.instance_exec(&e)}
|
124
|
+
|
125
|
+
sinatra.instance_variable_set("@#{self.instance_variable_name}", sinatra.instance_exec(&@init_block))
|
126
|
+
|
127
|
+
@after_blocks.each{|e| sinatra.instance_exec(&e)}
|
128
|
+
end
|
129
|
+
|
130
|
+
def sinatra_exec_get_block(sinatra, &block)
|
131
|
+
sinatra_init_data(:get, sinatra, &block)
|
132
|
+
sinatra.instance_exec(*sinatra.instance_variable_get("@__data"), &block)
|
133
|
+
end
|
134
|
+
|
135
|
+
def sinatra_exec_delete_block(sinatra, &block)
|
136
|
+
sinatra_init_data(:delete, sinatra, &block)
|
137
|
+
sinatra.instance_exec(*sinatra.instance_variable_get("@__data"), &block)
|
138
|
+
end
|
139
|
+
|
140
|
+
def sinatra_init_data(method, sinatra, &block)
|
141
|
+
raw_data = if [:put, :post].include?(method)
|
142
|
+
sinatra.request.body.rewind
|
143
|
+
HashWithIndifferentAccess.new(JSON.parse(sinatra.request.body.read))
|
144
|
+
elsif [:get, :delete].include?(method)
|
145
|
+
sinatra.params
|
146
|
+
else
|
147
|
+
{}
|
148
|
+
end
|
149
|
+
|
150
|
+
sinatra.instance_variable_set("@__raw_data", raw_data)
|
151
|
+
sinatra.instance_variable_set("@__data", raw_data.values_at(*block.parameters.map(&:last)))
|
152
|
+
end
|
153
|
+
|
154
|
+
def sinatra_exec_put_block(sinatra, &block)
|
155
|
+
sinatra_init_data(:put, sinatra, &block)
|
156
|
+
sinatra.instance_exec(*sinatra.instance_variable_get("@__data"), &block)
|
157
|
+
end
|
158
|
+
|
159
|
+
def sinatra_exec_post_block(sinatra, &block)
|
160
|
+
sinatra_init_data(:post, sinatra, &block)
|
161
|
+
res = sinatra.instance_exec(*sinatra.instance_variable_get("@__data"), &block)
|
162
|
+
sinatra.instance_variable_set("@#{self.instance_variable_name}", res)
|
163
|
+
# TODO: do we need to check for existing variables here?
|
164
|
+
# sinatra_set_instance_variable(sinatra, self.instance_variable_name, res)
|
165
|
+
end
|
166
|
+
|
167
|
+
def sinatra_response_type(response)
|
168
|
+
(response.is_a?(ActiveModel::Errors) || (response.respond_to?(:errors) && !response.errors.empty?)) ? :error : (response.is_a?(Nested::Redirect) ? :redirect : :data)
|
169
|
+
end
|
170
|
+
|
171
|
+
def sinatra_response(sinatra, method)
|
172
|
+
response = if sinatra.errors.empty?
|
173
|
+
sinatra.instance_variable_get("@#{self.instance_variable_name}")
|
174
|
+
else
|
175
|
+
sinatra.errors
|
176
|
+
end
|
177
|
+
|
178
|
+
response = self.send(:"sinatra_response_create_#{sinatra_response_type(response)}", sinatra, response, method)
|
179
|
+
|
180
|
+
case response
|
181
|
+
when Nested::Redirect then
|
182
|
+
sinatra.redirect(response.url)
|
183
|
+
when String then
|
184
|
+
response
|
185
|
+
else
|
186
|
+
response.to_json
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def sinatra_response_create_redirect(sinatra, response, method)
|
191
|
+
response
|
192
|
+
end
|
193
|
+
|
194
|
+
def sinatra_response_create_data(sinatra, response, method)
|
195
|
+
data = if response && (is_a?(Many) || response.is_a?(Array)) && method != :post
|
196
|
+
response.to_a.map{|e| sinatra.instance_exec(e, &@serializer.serialize) }
|
197
|
+
else
|
198
|
+
sinatra.instance_exec(response, &@serializer.serialize)
|
199
|
+
end
|
200
|
+
|
201
|
+
{data: data, ok: true}
|
202
|
+
end
|
203
|
+
|
204
|
+
def sinatra_errors_to_hash(errors)
|
205
|
+
errors.to_hash.inject({}) do |memo, e|
|
206
|
+
memo[e[0]] = e[1][0]
|
207
|
+
memo
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def sinatra_response_create_error(sinatra, response, method)
|
212
|
+
errors = response.is_a?(ActiveModel::Errors) ? response : response.errors
|
213
|
+
{data: sinatra_errors_to_hash(errors), ok: false}
|
214
|
+
end
|
215
|
+
|
216
|
+
def create_sinatra_route(method, action, method_if_block, &block)
|
217
|
+
@actions << {method: method, action: action, block: block}
|
218
|
+
|
219
|
+
resource = self
|
220
|
+
|
221
|
+
route = resource.route(action)
|
222
|
+
|
223
|
+
@sinatra.send(method, route) do
|
224
|
+
def self.error(message)
|
225
|
+
errors.add(:base, message)
|
226
|
+
end
|
227
|
+
|
228
|
+
def self.errors
|
229
|
+
@__errors ||= ActiveModel::Errors.new({})
|
230
|
+
end
|
231
|
+
|
232
|
+
begin
|
233
|
+
content_type :json
|
234
|
+
|
235
|
+
resource.self_and_parents.reverse.each do |res|
|
236
|
+
res.sinatra_init(self)
|
237
|
+
end
|
238
|
+
|
239
|
+
raise "method_if_block returns false for method: #{method}, action: #{action}, resource: #{resource.name}" unless instance_exec(&method_if_block)
|
240
|
+
|
241
|
+
resource.send(:"sinatra_exec_#{method}_block", self, &block)
|
242
|
+
|
243
|
+
resource.sinatra_response(self, method)
|
244
|
+
rescue Exception => e
|
245
|
+
context_arr = []
|
246
|
+
context_arr << "route: #{route}"
|
247
|
+
context_arr << "method: #{method}"
|
248
|
+
context_arr << "action: #{action}"
|
249
|
+
|
250
|
+
context_arr << "resource: #{resource.name} (#{resource.class.name})"
|
251
|
+
resource_object = instance_variable_get("@#{resource.instance_variable_name}")
|
252
|
+
context_arr << "@#{resource.instance_variable_name}: #{resource_object.inspect}"
|
253
|
+
|
254
|
+
parent = resource.try(:parent)
|
255
|
+
|
256
|
+
if parent
|
257
|
+
context_arr << "parent: #{parent.name} (#{parent.class.name})"
|
258
|
+
parent_object = instance_variable_get("@#{parent.instance_variable_name}")
|
259
|
+
context_arr << "@#{parent.instance_variable_name}: #{parent_object.inspect}"
|
260
|
+
end
|
261
|
+
|
262
|
+
puts context_arr.join("\n")
|
263
|
+
raise e
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Nested
|
2
|
+
class Serializer
|
3
|
+
attr_accessor :includes, :excludes
|
4
|
+
|
5
|
+
def initialize(includes=[])
|
6
|
+
@includes = includes.clone
|
7
|
+
@excludes = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def +(field)
|
11
|
+
field = field.is_a?(SerializerField) ? field : SerializerField.new(field, ->{ true })
|
12
|
+
|
13
|
+
@includes << field unless @includes.detect{|e| e.name == field.name}
|
14
|
+
@excludes = @excludes.reject{|e| e.name == field.name}
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def -(field)
|
19
|
+
field = field.is_a?(SerializerField) ? field : SerializerField.new(field, ->{ true })
|
20
|
+
|
21
|
+
@excludes << field unless @excludes.detect{|e| e.name == field.name}
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def serialize
|
26
|
+
this = self
|
27
|
+
->(obj) do
|
28
|
+
excludes = this.excludes.select{|e| instance_exec(&e.condition)}
|
29
|
+
|
30
|
+
this.includes.reject{|e| excludes.detect{|e2| e2.name == e.name}}.inject({}) do |memo, field|
|
31
|
+
if instance_exec(&field.condition)
|
32
|
+
case field.name
|
33
|
+
when Symbol
|
34
|
+
memo[field.name] = obj.is_a?(Hash) ? obj[field.name] : obj.send(field.name)
|
35
|
+
when Hash
|
36
|
+
field_name, proc = field.name.to_a.first
|
37
|
+
memo[field_name] = instance_exec(obj, &proc)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
memo
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Nested
|
2
|
+
class Singleton < Resource
|
3
|
+
include WithMany
|
4
|
+
|
5
|
+
def default_init_block
|
6
|
+
if parent
|
7
|
+
Proc.new{ instance_variable_get("@#{@__resource.parent.instance_variable_name}").send(@__resource.name) }
|
8
|
+
else
|
9
|
+
Proc.new { nil }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Nested
|
2
|
+
module WithMany
|
3
|
+
def many(name, init_block=nil, &block)
|
4
|
+
many_if(PROC_TRUE, name, init_block, &block)
|
5
|
+
end
|
6
|
+
|
7
|
+
def many_if(resource_if_block, name, init_block=nil, &block)
|
8
|
+
child_resource(name, Many, resource_if_block, init_block, &block)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Nested
|
2
|
+
module WithSingleton
|
3
|
+
def singleton(name, init_block=nil, &block)
|
4
|
+
singleton_if(PROC_TRUE, name, init_block, &block)
|
5
|
+
end
|
6
|
+
|
7
|
+
def singleton_if(resource_if_block, name, init_block=nil, &block)
|
8
|
+
child_resource(name, Singleton, resource_if_block, init_block, &block)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|