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.
data/lib/nested.rb CHANGED
@@ -1,576 +1,26 @@
1
- require "json"
2
-
3
1
  module Nested
2
+ PROC_TRUE = Proc.new{ true }
3
+ end
4
4
 
5
- class ManyInManyError < StandardError
6
- end
7
-
8
- class SingletonAndCollectionError < StandardError
9
- end
10
-
11
- class NameMissingError < StandardError
12
- end
13
-
14
- class Redirect
15
- attr_reader :url
16
- def initialize(url)
17
- @url = url
18
- end
19
- end
20
-
21
- class SerializerField
22
- attr_accessor :name, :condition
23
- def initialize(name, condition)
24
- @name = name
25
- @condition = condition
26
- end
27
- end
28
-
29
- class Serializer
30
- attr_accessor :includes, :excludes
31
-
32
- def initialize(includes=[])
33
- @includes = includes.clone
34
- @excludes = []
35
- end
36
-
37
- def +(field)
38
- field = field.is_a?(SerializerField) ? field : SerializerField.new(field, ->{ true })
39
-
40
- @includes << field unless @includes.detect{|e| e.name == field.name}
41
- @excludes = @excludes.reject{|e| e.name == field.name}
42
- self
43
- end
44
-
45
- def -(field)
46
- field = field.is_a?(SerializerField) ? field : SerializerField.new(field, ->{ true })
47
-
48
- @excludes << field unless @excludes.detect{|e| e.name == field.name}
49
- self
50
- end
51
-
52
- def serialize
53
- this = self
54
- ->(obj) do
55
- excludes = this.excludes.select{|e| instance_exec(&e.condition)}
56
-
57
- this.includes.reject{|e| excludes.detect{|e2| e2.name == e.name}}.inject({}) do |memo, field|
58
- if instance_exec(&field.condition)
59
- case field.name
60
- when Symbol
61
- memo[field.name] = obj.is_a?(Hash) ? obj[field.name] : obj.send(field.name)
62
- when Hash
63
- field_name, proc = field.name.to_a.first
64
- memo[field_name] = instance_exec(obj, &proc)
65
- end
66
- end
67
- memo
68
- end
69
- end
70
- end
71
- end
72
-
73
- class Resource
74
- attr_reader :name, :parent, :actions, :resources, :serializer
75
-
76
- def initialize(sinatra, name, singleton, collection, parent, init_block)
77
- raise SingletonAndCollectionError.new if singleton && collection
78
- raise NameMissingError.new if (singleton || collection) && !name
79
-
80
- @sinatra = sinatra
81
- @name = name
82
- @singleton = singleton
83
- @collection = collection
84
- @parent = parent
85
- @resources = []
86
- @actions = []
87
- @init_block = init_block
88
- @run_blocks = []
89
-
90
- @serializer = Serializer.new(member? ? parent.serializer.includes : [])
91
- end
92
-
93
- def singleton?
94
- @singleton == true
95
- end
96
-
97
- def member?
98
- !singleton? && !collection?
99
- end
100
-
101
- def collection?
102
- @collection == true
103
- end
104
-
105
- def run(&block)
106
- @run_blocks << block
107
- self
108
- end
109
-
110
- def type
111
- if singleton?
112
- :singleton
113
- elsif member?
114
- :member
115
- elsif collection?
116
- :collection
117
- else
118
- raise "undefined"
119
- end
120
- end
121
-
122
- def serialize(*args)
123
- args.each {|arg| serializer + arg }
124
- serializer
125
- end
126
-
127
- def serialize_include_if(condition, *args)
128
- args.each {|arg| @serializer + SerializerField.new(arg, condition) }
129
- end
130
-
131
- def serialize_exclude_if(condition, *args)
132
- args.each {|arg| @serializer - SerializerField.new(arg, condition) }
133
- end
134
-
135
- def route_replace(route, args)
136
- args.each do |k, v|
137
- route = route.gsub(":#{k}", "#{v}")
138
- end
139
- route
140
- end
141
-
142
- def route(action=nil)
143
- "".tap do |r|
144
- r << @parent.route if @parent
145
-
146
-
147
- if singleton? || collection?
148
- r << "/#{@name}"
149
- else
150
- r << "/:#{@name}_id"
151
- end
152
-
153
- r << "/#{action}" if action
154
- end
155
- end
156
-
157
- def get(action=nil, &block)
158
- create_sinatra_route :get, action, &(block||proc {})
159
- end
160
-
161
- def post(action=nil, &block)
162
- create_sinatra_route :post, action, &block
163
- end
164
-
165
- def put(action=nil, &block)
166
- create_sinatra_route :put, action, &block
167
- end
168
-
169
- def delete(action=nil, &block)
170
- create_sinatra_route :delete, action, &block
171
- end
172
-
173
- def singleton(name, init_block=nil, &block)
174
- child_resource(name, true, false, init_block, &(block||Proc.new{ }))
175
- end
176
-
177
- def many(name, init_block=nil, &block)
178
- raise ManyInManyError.new "do not nest many in many" if collection?
179
- child_resource(name, false, true, init_block, &(block||Proc.new{ }))
180
- end
181
-
182
- def default_init_block_singleton
183
- if parent
184
- Proc.new{ instance_variable_get("@#{@__resource.parent.instance_variable_name}").send(@__resource.name) }
185
- else
186
- Proc.new { nil }
187
- end
188
- end
189
-
190
- def default_init_block_many
191
- if parent
192
- Proc.new{ instance_variable_get("@#{@__resource.parent.instance_variable_name}").send(@__resource.name) }
193
- else
194
- Proc.new { nil }
195
- end
196
- end
197
-
198
- def default_init_block_one
199
- if parent
200
- Proc.new do
201
- instance_variable_get("@#{@__resource.parent.instance_variable_name}")
202
- .where(id: params[:"#{@__resource.parent.name.to_s.singularize.to_sym}_id"])
203
- .first
204
- end
205
- else
206
- Proc.new { nil }
207
- end
208
- end
209
-
210
- def one(&block)
211
- raise "calling one() is only allowed within many() resource" unless collection?
212
- child_resource(self.name.to_s.singularize.to_sym, false, false, nil, &(block||Proc.new{ }))
213
- end
214
-
215
- def child_resource(name, singleton, collection, init_block, &block)
216
- Resource.new(@sinatra, name, singleton, collection, self, init_block)
217
- .tap{|r| r.instance_eval(&block)}
218
- .tap{|r| @resources << r}
219
- end
220
-
221
- def instance_variable_name
222
- member? ? parent.name.to_s.singularize.to_sym : @name
223
- end
224
-
225
- def parents
226
- (@parent ? @parent.parents + [@parent] : [])
227
- end
228
-
229
- def self_and_parents
230
- (self.parents + [self]).reverse
231
- end
232
-
233
- # --------------------------
234
-
235
- def sinatra_set_instance_variable(sinatra, name, value)
236
- raise "variable @#{name} already defined" if sinatra.instance_variable_defined?(:"@#{name}")
237
- sinatra.instance_variable_set(:"@#{name}", value)
238
- end
239
-
240
- def sinatra_init(sinatra)
241
- sinatra.instance_variable_set("@__resource", self)
242
-
243
- init_block = if @init_block
244
- @init_block
245
- else
246
- if singleton?
247
- default_init_block_singleton
248
- elsif collection?
249
- default_init_block_many
250
- else
251
- default_init_block_one
252
- end
253
- end
254
-
255
- sinatra.instance_variable_set("@#{self.instance_variable_name}", sinatra.instance_exec(&init_block))
256
-
257
- @run_blocks.each{|e| sinatra.instance_exec(&e)}
258
- end
259
-
260
- def sinatra_exec_get_block(sinatra, &block)
261
- sinatra_init_data(:get, sinatra, &block)
262
- sinatra.instance_exec(*sinatra.instance_variable_get("@__data"), &block)
263
- end
264
-
265
- def sinatra_exec_delete_block(sinatra, &block)
266
- sinatra_init_data(:delete, sinatra, &block)
267
- sinatra.instance_exec(*sinatra.instance_variable_get("@__data"), &block)
268
- end
269
-
270
- def sinatra_init_data(method, sinatra, &block)
271
- raw_data = if [:put, :post].include?(method)
272
- sinatra.request.body.rewind
273
- HashWithIndifferentAccess.new(JSON.parse(sinatra.request.body.read))
274
- elsif [:get, :delete].include?(method)
275
- sinatra.params
276
- else
277
- {}
278
- end
279
-
280
- sinatra.instance_variable_set("@__raw_data", raw_data)
281
- sinatra.instance_variable_set("@__data", raw_data.values_at(*block.parameters.map(&:last)))
282
- end
283
-
284
- def sinatra_exec_put_block(sinatra, &block)
285
- sinatra_init_data(:put, sinatra, &block)
286
- sinatra.instance_exec(*sinatra.instance_variable_get("@__data"), &block)
287
- end
288
-
289
- def sinatra_exec_post_block(sinatra, &block)
290
- sinatra_init_data(:post, sinatra, &block)
291
- res = sinatra.instance_exec(*sinatra.instance_variable_get("@__data"), &block)
292
- sinatra.instance_variable_set("@#{self.instance_variable_name}", res)
293
- # TODO: do we need to check for existing variables here?
294
- # sinatra_set_instance_variable(sinatra, self.instance_variable_name, res)
295
- end
296
-
297
- def sinatra_response_type(response)
298
- (response.is_a?(ActiveModel::Errors) || (response.respond_to?(:errors) && !response.errors.empty?)) ? :error : (response.is_a?(Nested::Redirect) ? :redirect : :data)
299
- end
300
-
301
- def sinatra_response(sinatra, method)
302
- response = if sinatra.errors.empty?
303
- sinatra.instance_variable_get("@#{self.instance_variable_name}")
304
- else
305
- sinatra.errors
306
- end
307
-
308
- response = self.send(:"sinatra_response_create_#{sinatra_response_type(response)}", sinatra, response, method)
309
-
310
- case response
311
- when Nested::Redirect then
312
- sinatra.redirect(response.url)
313
- when String then
314
- response
315
- else
316
- response.to_json
317
- end
318
- end
319
-
320
- def sinatra_response_create_redirect(sinatra, response, method)
321
- response
322
- end
323
-
324
- def sinatra_response_create_data(sinatra, response, method)
325
- data = if response && (collection? || response.is_a?(Array)) && method != :post
326
- response.to_a.map{|e| sinatra.instance_exec(e, &@serializer.serialize) }
327
- else
328
- sinatra.instance_exec(response, &@serializer.serialize)
329
- end
330
-
331
- {data: data, ok: true}
332
- end
333
-
334
- def sinatra_errors_to_hash(errors)
335
- errors.to_hash.inject({}) do |memo, e|
336
- memo[e[0]] = e[1][0]
337
- memo
338
- end
339
- end
340
-
341
- def sinatra_response_create_error(sinatra, response, method)
342
- errors = response.is_a?(ActiveModel::Errors) ? response : response.errors
343
- {data: sinatra_errors_to_hash(errors), ok: false}
344
- end
345
-
346
- def create_sinatra_route(method, action, &block)
347
- @actions << {method: method, action: action, block: block}
348
-
349
- resource = self
350
-
351
- route = resource.route(action)
352
-
353
- @sinatra.send(method, route) do
354
- def self.error(message)
355
- errors.add(:base, message)
356
- end
357
-
358
- def self.errors
359
- @__errors ||= ActiveModel::Errors.new({})
360
- end
361
-
362
- begin
363
- content_type :json
364
-
365
- resource.self_and_parents.reverse.each do |res|
366
- res.sinatra_init(self)
367
- end
368
-
369
- resource.send(:"sinatra_exec_#{method}_block", self, &block)
370
-
371
- resource.sinatra_response(self, method)
372
- rescue Exception => e
373
- context_arr = []
374
- context_arr << "route: #{route}"
375
- context_arr << "method: #{method}"
376
- context_arr << "action: #{action}"
377
-
378
- context_arr << "resource: #{resource.name} (#{resource.type})"
379
- resource_object = instance_variable_get("@#{resource.instance_variable_name}")
380
- context_arr << "@#{resource.instance_variable_name}: #{resource_object.inspect}"
381
-
382
- parent = resource.try(:parent)
383
-
384
- if parent
385
- context_arr << "parent: #{parent.try(:name)} (#{parent.try(:type)})"
386
- parent_object = instance_variable_get("@#{parent.try(:instance_variable_name)}")
387
- context_arr << "@#{parent.try(:instance_variable_name)}: #{parent_object.inspect}"
388
- end
389
-
390
- puts context_arr.join("\n")
391
- raise e
392
- end
393
- end
394
- end
395
- end
396
-
397
- module Angular
398
- def self.extended(base)
399
- base.send(:extend, Nested::Angular::Sinatra)
400
- end
401
-
402
- module Sinatra
403
- # def create_resource(name, singleton, collection, &block)
404
- def create_resource(*args, &block)
405
- angularize(super)
406
- end
407
-
408
- def nested_angular_config(config=nil)
409
- if config
410
- @nested_angular_config = config
411
- else
412
- @nested_angular_config ||= {}
413
- end
414
- end
415
-
416
- end
417
-
418
- def angular_add_functions(js, resource)
419
- resource.actions.each do |e|
420
- method, action, block = e.values_at :method, :action, :block
421
- block_args = block.parameters.map(&:last)
422
-
423
- fun_name = Nested::JsUtil::generate_function_name(resource, method, action)
424
-
425
- args = Nested::JsUtil::function_arguments(resource)
426
-
427
- route_args = args.inject({}) do |memo, e|
428
- idx = args.index(e)
429
- memo[:"#{e}_id"] = "'+(typeof(values[#{idx}]) == 'number' ? values[#{idx}].toString() : (values[#{idx}].id || values[#{idx}]))+'"
430
- memo
431
- end
432
- route = "#{self.nested_config[:prefix]}" + resource.route_replace(resource.route(action), route_args)
433
- when_args = args.map{|a| "$q.when(#{a})"}
434
-
435
- js << " impl.#{fun_name}Url = function(#{args.join(',')}){"
436
- js << " var deferred = $q.defer()"
437
- js << " $q.all([#{when_args.join(',')}]).then(function(values){"
438
- js << " deferred.resolve('#{route}')"
439
- js << " })"
440
- js << " return deferred.promise"
441
- js << " }"
442
-
443
- if [:get, :delete].include?(method)
444
- args << "data" if !block_args.empty?
445
-
446
- js << " impl.#{fun_name} = function(#{args.join(',')}){"
447
- js << " var deferred = $q.defer()"
448
- js << " $q.all([#{when_args.join(',')}]).then(function(values){"
449
- js << " $http({"
450
- js << " method: '#{method}', "
451
- js << (" url: '#{route}'" + (block_args.empty? ? "" : ","))
452
- js << " params: data" unless block_args.empty?
453
- js << " })"
454
- js << " .success(function(responseData){"
455
- js << " deferred[responseData.ok ? 'resolve' : 'reject'](responseData.data)"
456
- js << " })"
457
- js << " .error(function(){ deferred.reject() })"
458
- js << " });"
459
- js << " return deferred.promise"
460
- js << " }"
461
- elsif method == :post
462
- js << " impl.#{fun_name} = function(#{(args+['data']).join(',')}){"
463
- js << " var deferred = $q.defer()"
464
- js << " $q.all([#{when_args.join(',')}]).then(function(values){"
465
- js << " $http({method: '#{method}', url: '#{route}', data: data})"
466
- js << " .success(function(responseData){"
467
- js << " deferred[responseData.ok ? 'resolve' : 'reject'](responseData.data)"
468
- js << " })"
469
- js << " .error(function(){ deferred.reject() })"
470
- js << " });"
471
- js << " return deferred.promise"
472
- js << " }"
473
- elsif method == :put
474
- args << "data" if args.empty? || !block_args.empty?
475
-
476
- js << " impl.#{fun_name} = function(#{args.join(',')}){"
477
- js << " var deferred = $q.defer()"
478
- js << " $q.all([#{when_args.join(',')}]).then(function(values){"
479
- js << " $http({method: '#{method}', url: '#{route}', data: #{args.last}})"
480
- js << " .success(function(responseData){"
481
- js << " deferred[responseData.ok ? 'resolve' : 'reject'](responseData.data)"
482
- js << " })"
483
- js << " .error(function(){ deferred.reject() })"
484
- js << " });"
485
- js << " return deferred.promise"
486
- js << " }"
487
- end
488
- end
489
-
490
- resource.resources.each {|r| angular_add_functions(js, r) }
491
- end
492
-
493
- def angularize(resource)
494
- js = []
495
-
496
- module_name = "nested_#{resource.name}".camelcase(:lower)
497
-
498
- js << "angular.module('#{module_name}#{nested_angular_config[:service_suffix]}', [])"
499
- js << ".factory('#{resource.name.to_s.camelcase.capitalize}#{nested_angular_config[:service_suffix]}', function($http, $q){"
500
-
501
- js << " var impl = {}"
502
- angular_add_functions(js, resource)
503
- js << " return impl"
504
-
505
- js << "})"
506
-
507
- response_transform = nested_angular_config[:response_transform] || ->(code){ code }
508
-
509
- get "/#{resource.name}.js" do
510
- content_type :js
511
- instance_exec(js.join("\n"), &response_transform)
512
- end
513
- end
514
- end
515
-
516
- module JsUtil
517
- def self.generate_function_name(resource, method, action)
518
- arr = []
5
+ require "json"
6
+ require "sinatra/base"
519
7
 
520
- arr << "update" if method == :put
521
- arr << "create" if method == :post
522
- arr << "destroy" if method == :delete
8
+ require "nested/js"
523
9
 
524
- all = resource.self_and_parents.reverse
10
+ require "nested/redirect"
525
11
 
526
- all.each do |e|
527
- if e.collection?
528
- if e == all.last
529
- if method == :post
530
- arr << e.name.to_s.singularize.to_sym
531
- else
532
- arr << e.name
533
- end
534
- else
535
- arr << e.name unless all[all.index(e) + 1].member?
536
- end
537
- else
538
- arr << e.name
539
- end
540
- end
12
+ require "nested/with_many"
13
+ require "nested/with_singleton"
541
14
 
542
- arr << action if action
15
+ require "nested/resource"
543
16
 
544
- arr.map(&:to_s).join("_").camelcase(:lower)
545
- end
17
+ require "nested/one"
18
+ require "nested/singleton"
19
+ require "nested/many"
546
20
 
547
- def self.function_arguments(resource)
548
- resource
549
- .self_and_parents.select{|r| r.member?}
550
- .map{|r| r.name || r.parent.name}
551
- .map(&:to_s)
552
- .map(&:singularize)
553
- .reverse
554
- end
555
- end
21
+ require "nested/serializer"
22
+ require "nested/serializer_field"
556
23
 
557
- module Sinatra
558
- def nested_config(config=nil)
559
- if config
560
- @nested_config = config
561
- else
562
- @nested_config ||= {}
563
- end
564
- end
565
- def singleton(name, init_block=nil, &block)
566
- create_resource(name, true, false, init_block, &block)
567
- end
568
- def many(name, init_block=nil, &block)
569
- create_resource(name, false, true, init_block, &block)
570
- end
571
- def create_resource(name, singleton, collection, init_block, &block)
572
- ::Nested::Resource.new(self, name, singleton, collection, nil, init_block).tap{|r| r.instance_eval(&block) }
573
- end
574
- end
24
+ require "nested/app"
575
25
 
576
- end
26
+ require "nested/integration/angular"
data/nested.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "nested"
3
- s.version = "0.0.25"
3
+ s.version = "0.0.26"
4
4
 
5
5
  s.authors = ["Jan Zimmek"]
6
6
  s.email = %q{jan.zimmek@web.de}