nested 0.0.25 → 0.0.26

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: da43c009c3ff14c2773df3a11e9ea8d5ae2f8de0
4
- data.tar.gz: f63ecccb28294309d354362ca5bcff05937dd11a
3
+ metadata.gz: f3204fac8586400602f52773cfa5cd557acaaf15
4
+ data.tar.gz: 4e017941f4c4bbc7ae02ee77df82a9b2944d26fd
5
5
  SHA512:
6
- metadata.gz: dc57e5a73bea891b475bc0810cecd6b9e3f48f3710d173f6b30c358c5345bf19e5075c9e8424effaf46aa5196b23951b522e09e3c0e717f2c8b314ff3049a469
7
- data.tar.gz: 3b753f26a8ac5d8d57133b707637e78ce1ce80751ae8132b3979be3037edb1030ab72415a789a74e4409f3ca9021fa158b5da150c6a55e0c5d3e83f14c6389d1
6
+ metadata.gz: 781858c122e8512d79e0fde3f0753ff97e473636c38e3bd5f4c5146a05b5954d0874ffb2a7980fbc04b944653b751026bc6506179d4732b2833d5b8325951b8f
7
+ data.tar.gz: db049be70b1b9899c2a08301c34534f6bdca917f470fe3af50bf3d6f9a73b0851c473618e8d54f38a296838c9353c3cbba6a960c84c64ec31b6f7574ded308ac
data/Gemfile CHANGED
@@ -4,5 +4,6 @@ source 'https://rubygems.org'
4
4
 
5
5
  gem "activesupport"
6
6
  gem "activerecord"
7
+ gem "sinatra"
7
8
  gem "mocha"
8
9
  gem "json"
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
@@ -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,8 @@
1
+ module Nested
2
+ class Redirect
3
+ attr_reader :url
4
+ def initialize(url)
5
+ @url = url
6
+ end
7
+ end
8
+ 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,9 @@
1
+ module Nested
2
+ class SerializerField
3
+ attr_accessor :name, :condition
4
+ def initialize(name, condition)
5
+ @name = name
6
+ @condition = condition
7
+ end
8
+ end
9
+ 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