grape-reload 0.0.3 → 0.0.4

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: d23fb6507e6ffdadf978ae7cafa32ad6a49b7b09
4
- data.tar.gz: 4cdb689e50c6f760afb8ea2fe660b3c4d3971cea
3
+ metadata.gz: cf764d2710409ccf448fb1a9b1878611ae510b28
4
+ data.tar.gz: 74bb786cd48bd89ac78e95e955680bb695e30846
5
5
  SHA512:
6
- metadata.gz: ee472baaa8f3a405e193940073a8ea0734501179432b18f33e6d625e58728d06cf884e318430581f720c6d2598d7ea5811b089a1fc5e650a62fef5d79fa5e846
7
- data.tar.gz: e6d6ce030ee2f4f6aff852817276cb3597fb852cbf0c178505a069b622aa5cbf662049450e20e356cb66555288817d1be149d5b618da21fe586a87a647172b60
6
+ metadata.gz: 9b918ce662a102851066936b2bb1c3a534af0dfbee84a900e0692f80095ef67905bdf47c8c7773c26619a6b64c8e826403bad8bfe184734cde1c8c035fc96b36
7
+ data.tar.gz: 21a8509583dc86cb23b3747536c32090fb88deeb415bb44ff277797193a95f6368d181ae51865e43d9f96fb4a72d4d92766cb6ca82e83ef2caf93844983d4291
data/README.md CHANGED
@@ -23,21 +23,22 @@ Or install it yourself as:
23
23
 
24
24
  In your config.ru you use Grape::RackBuilder to mount your apps:
25
25
 
26
- builder = Grape::RackBuilder.setup do
26
+ Grape::RackBuilder.setup do
27
27
  logger Logger.new(STDOUT)
28
28
  add_source_path File.expand_path('**/*.rb', YOUR_APP_ROOT)
29
29
  reload_threshold 1 # Reload sources not often one second
30
+ force_reloading true # Force reloading for any environment (not just dev), useful for testing
30
31
  mount 'Your::App', to: '/'
31
32
  mount 'Your::App1', to: '/app1'
32
33
  end
33
34
 
34
- run builder.boot!.application
35
+ run Grape::RackBuilder.boot!.application
35
36
 
36
- Grape::Reload will resolve all class dependencies and load your files in appropriate order, so you don't need to include 'require' or 'require_relative' for your app classes.
37
+ Grape::Reload will resolve all class dependencies and load your files in appropriate order, so you don't need to include 'require' or 'require_relative' in your sources.
37
38
 
38
39
  ## Restrictions:
39
-
40
- If you want to monkey-patch class in your code for any reason, you should use
40
+ ### Monkey patching
41
+ If you want to monkey-patch class in code, you want to be reloaded, for any reason, you should use
41
42
 
42
43
  AlreadyDefined.class_eval do
43
44
  end
@@ -45,18 +46,53 @@ If you want to monkey-patch class in your code for any reason, you should use
45
46
  instead of
46
47
 
47
48
  class AlreadyDefined
48
- end
49
+ end
49
50
 
50
51
  because it confuses dependency resolver
51
52
 
53
+ ### Full-qualified const name usage
54
+ Consider code
55
+
56
+ require 'some_file' # (declares SomeModule::SomeClass)
57
+
58
+ here_is_your_code(SomeClass)
59
+
60
+ Ruby will resolve SomeClass to SomeModule::SomeClass in runtime.
61
+ Dependency resolver will display an error, because it expects you to
62
+ use full-qualified class name in this situation.
63
+ Anyway, it would not raise exception anymore (since e5b58f4)
64
+
65
+ here_is_your_code(SomeModule::SomeClass)
66
+
67
+ ### Other restrictions
68
+
69
+ Avoid declaring constants as follows
70
+
71
+ class AlreadyDeclaredModule::MyClass
72
+ end
73
+
74
+ use
75
+
76
+ module AlreadyDeclaredModule
77
+ class MyClass
78
+ end
79
+ end
80
+
81
+ instead
82
+
52
83
  ## Known issues
53
84
 
54
85
  * It still lacks of good design :(
55
86
  * MOAR TESTS!!!!111
56
87
 
88
+ ## TODO
89
+
90
+ * example Grape application with Grape::Reload
91
+ * Spork integration example
92
+
57
93
  ## Contributing
58
94
 
59
- 1. Fork it ( https://github.com/AMar4enko/grape-reload/fork )
95
+ 1. Fork it ( https://github.com/AlexYankee/grape-reload/fork )
60
96
  2. Create your feature branch (`git checkout -b my-new-feature`)
61
97
  3. Commit your changes (`git commit -am 'Add some feature'`)
62
98
  4. Push to the branch (`git push origin my-new-feature`)
@@ -117,7 +117,7 @@ module Grape
117
117
  filenames.each {|filename| Grape::RackBuilder.logger.error("Unresolved const reference #{klass} from: #{filename}".colorize(:red)) }
118
118
  end
119
119
 
120
- raise UnresolvedDependenciesError if unresolved_classes.any?
120
+ Grape::RackBuilder.logger.error("One or more unresolved dependencies found".colorize(:red)) if unresolved_classes.any?
121
121
  end
122
122
  end
123
123
 
@@ -36,47 +36,68 @@ end
36
36
  module Grape
37
37
  module Reload
38
38
  module AutoreloadInterceptor
39
- [:set, :nest, :route, :imbue, :mount, :desc, :params, :helpers, :format, :formatter, :parser, :error_formatter, :content_type].each do |method|
40
- eval <<METHOD
41
- def #{method}(*args, &block)
42
- class_declaration << [:#{method},args,block]
39
+ extend ActiveSupport::Concern
40
+
41
+ def add_head_not_allowed_methods_and_options_methods(*args, &block)
42
+ self.class.skip_declaration = true
43
+ super(*args, &block)
44
+ self.class.skip_declaration = false
45
+ end
46
+
47
+ module ClassMethods
48
+ attr_accessor :skip_declaration
49
+
50
+ def namespace(*args, &block)
51
+ @skip_declaration = true
52
+ class_declaration << [:namespace,args,block]
43
53
  super(*args, &block)
54
+ @skip_declaration = false
44
55
  end
56
+
57
+ [:set, :imbue, :mount, :route, :desc, :params, :helpers, :format, :formatter, :parser, :error_formatter, :content_type].each do |method|
58
+ eval <<METHOD
59
+ def #{method}(*args, &block)
60
+ class_declaration << [:#{method},args,block] unless @skip_declaration
61
+ super(*args, &block)
62
+ end
45
63
  METHOD
46
- end
64
+ end
47
65
 
48
- def reinit!
49
- declaration = class_declaration.dup
50
- @class_decl = []
51
- endpoints.each { |e| e.options[:app].reinit! if e.options[:app] }
52
- reset!
53
- declaration.each {|decl|
54
- send(decl[0],*deep_reconstantize.call(decl[1]),&decl[2])
55
- }
56
- change!
57
- end
66
+ def reinit!
67
+ declaration = class_declaration.dup
68
+ @class_decl = []
69
+ endpoints_cache = endpoints
70
+ reset!
71
+ endpoints_cache.each { |e| e.options[:app].reinit! if e.options[:app].respond_to?('reinit!') }
58
72
 
59
- def recursive_!
73
+ declaration.each {|decl|
74
+ send(decl[0],*deep_reconstantize.call(decl[1]),&decl[2])
75
+ }
76
+ change!
77
+ end
60
78
 
61
- end
62
- private
63
- def class_declaration
64
- @class_decl ||= []
65
- end
66
- def deep_reconstantize
67
- proc = ->(value) {
68
- case value
69
- when Hash
70
- Hash[value.each_pair.map { |k,v| [proc.call(k), proc.call(v)] }]
71
- when Array
72
- value.map { |v| proc.call(v) }
73
- when Class
74
- return if value.to_s[0,2] == '#<'
75
- value.to_s.constantize
76
- else
77
- value
78
- end
79
- }
79
+ def recursive_!
80
+
81
+ end
82
+ private
83
+ def class_declaration
84
+ @class_decl ||= []
85
+ end
86
+ def deep_reconstantize
87
+ proc = ->(value) {
88
+ case value
89
+ when Hash
90
+ Hash[value.each_pair.map { |k,v| [proc.call(k), proc.call(v)] }]
91
+ when Array
92
+ value.map { |v| proc.call(v) }
93
+ when Class
94
+ return value if value.to_s[0,2] == '#<'
95
+ value.to_s.constantize
96
+ else
97
+ value
98
+ end
99
+ }
100
+ end
80
101
  end
81
102
  end
82
103
  end
@@ -87,7 +108,7 @@ Grape::API.singleton_class.class_eval do
87
108
  alias_method :settings_shadowed, :settings
88
109
  def inherited(*args)
89
110
  inherited_shadowed(*args)
90
- args.first.singleton_class.class_eval do
111
+ args.first.class_eval do
91
112
  include Grape::Reload::AutoreloadInterceptor
92
113
  end
93
114
  end
@@ -0,0 +1,7 @@
1
+ module Rack
2
+ class Cascade
3
+ def reinit!
4
+ @apps.map{|app| app.reinit! if app.respond_to?('reinit!') }
5
+ end
6
+ end
7
+ end
@@ -26,9 +26,9 @@ module Grape
26
26
  end
27
27
 
28
28
  class Config
29
- attr_accessor :mounts, :sources, :options
29
+ attr_accessor :mounts, :sources, :options, :force_reloading
30
30
 
31
- {environment: RACK_ENV, reload_threshold: 1, logger: LoggingStub}.each_pair do |attr, default|
31
+ {environment: RACK_ENV, reload_threshold: 1, logger: LoggingStub, force_reloading: false}.each_pair do |attr, default|
32
32
  attr_accessor attr
33
33
  define_method(attr) { |value = nil|
34
34
  @options ||= {}
@@ -41,6 +41,10 @@ module Grape
41
41
  (@sources ||= []) << glob
42
42
  end
43
43
 
44
+ def use(*args, &block)
45
+ middleware << [args, block]
46
+ end
47
+
44
48
  def mount(app_class, options)
45
49
  mounts << MountConfig.new(
46
50
  app_class: app_class,
@@ -52,6 +56,10 @@ module Grape
52
56
  def mounts
53
57
  @mounts ||= []
54
58
  end
59
+
60
+ def middleware
61
+ @middleware ||= []
62
+ end
55
63
  end
56
64
 
57
65
  module ClassMethods
@@ -61,6 +69,7 @@ module Grape
61
69
  end
62
70
 
63
71
  def boot!
72
+ @rack_app = nil
64
73
  Grape::Reload::Watcher.setup(sources: Grape::Reload::Sources.new(config.sources))
65
74
  self
66
75
  end
@@ -68,11 +77,17 @@ module Grape
68
77
  def application
69
78
  return @rack_app if @rack_app
70
79
  mounts = config.mounts
80
+ middleware = config.middleware
81
+ force_reloading = config.force_reloading
71
82
  environment = config.environment
72
83
  reload_threshold = config.reload_threshold
73
84
  @rack_app = ::Rack::Builder.new do
85
+ middleware.each do |parameters|
86
+ parameters.length == 1 ? use(*parameters.first) : use(*parameters.first, &parameters.last)
87
+ end
88
+
74
89
  mounts.each_with_index do |m|
75
- if environment == 'development'
90
+ if (environment == 'development') || force_reloading
76
91
  r = Rack::Builder.new
77
92
  r.use Grape::ReloadMiddleware[reload_threshold]
78
93
  r.run m.app_class
@@ -1,5 +1,5 @@
1
1
  module Grape
2
2
  module Reload
3
- VERSION = "0.0.3"
3
+ VERSION = "0.0.4"
4
4
  end
5
5
  end
@@ -17,7 +17,7 @@ module Grape
17
17
  return unless options[:force] || file_changed?(file)
18
18
 
19
19
  Storage.prepare(file) # might call #safe_load recursively
20
- logger.debug((file_new?(file) ? "loading" : "reloading") + "#{file}" )
20
+ logger.debug((file_new?(file) ? "loading" : "reloading") + " #{file}" )
21
21
  begin
22
22
  with_silence{ require(file) }
23
23
  Storage.commit(file)
@@ -102,9 +102,8 @@ module Grape
102
102
  changed_files_sorted.each{|f| safe_load(f)}
103
103
  end
104
104
  changed_files_sorted.map{|f| @sources.dependent_classes(f) }.flatten.uniq.each {|class_name|
105
- if (klass = class_name.constantize) < Grape::API
106
- klass.reinit!
107
- end
105
+ next unless (klass = class_name.constantize).kind_of? Class
106
+ klass.reinit! if klass.respond_to?('reinit!')
108
107
  }
109
108
  end
110
109
 
data/lib/grape/reload.rb CHANGED
@@ -2,3 +2,4 @@ require "grape/reload/version"
2
2
  require 'core_ext/string/colorize'
3
3
  require 'core_ext/object_space'
4
4
  require "grape/reload/rack_builder"
5
+ require "grape/reload/rack"
@@ -53,7 +53,7 @@ class TraversingResult
53
53
  end
54
54
 
55
55
  variants << [ const_ary ]
56
- @used << variants.map{|v| v.join('::')}
56
+ @used << (variants.map{|v| v.join('::')} << '::' + const)
57
57
  end
58
58
  else
59
59
  @used << const
@@ -107,7 +107,7 @@ class TraversingResult
107
107
  used = used.map {|variants| variants.map{|v| (v.start_with?('::') ? '' : (namespace || '') + '::') + v }}
108
108
  end
109
109
 
110
- result[:used] = result[:used].concat(used)
110
+ result[:used] = result[:used].concat(used).uniq
111
111
 
112
112
  result
113
113
  end
@@ -135,7 +135,7 @@ class ASTEntity
135
135
  else
136
136
  # Code position for identifier
137
137
  return if node_ary.kind_of?(Array) and (node_ary.size == 2) and node_ary[0].kind_of?(Integer) and node_ary[1].kind_of?(Integer)
138
- node_ary.map{|n| load(n) }
138
+ node_ary.map{|n| node_for(n) }
139
139
  end
140
140
  end
141
141
  end
@@ -157,7 +157,7 @@ class ASTEntity
157
157
  when ASTEntity
158
158
  e.collect_constants(result, context || (TraversingContext.new)) unless e.nil?
159
159
  when Array
160
- e.map{|e| e.collect_constants(result, context || (TraversingContext.new)) unless e.nil? }
160
+ e.flatten.map{|e| e.collect_constants(result, context || (TraversingContext.new)) unless e.nil? }
161
161
  else
162
162
  end
163
163
  } unless @body.nil?
@@ -172,6 +172,27 @@ class ASTProgramDecl < ASTEntity
172
172
  end
173
173
  end
174
174
 
175
+ class ASTDef < ASTEntity
176
+ def self.ripper_id; :def end
177
+ def collect_constants(result, context)
178
+ result
179
+ end
180
+ end
181
+
182
+ class ASTCommand < ASTEntity
183
+ def self.ripper_id; :command end
184
+ def initialize(*args)
185
+ @command = args.first[1]
186
+ super(*args)
187
+ end
188
+ def collect_constants(result, context)
189
+ @old_stop_collect_constants = context[:stop_collect_constants]
190
+ context[:stop_collect_constants] = nil unless %w{desc mount params}.index(@command).nil?
191
+ ret = super(result, context)
192
+ context[:stop_collect_constants] = @old_stop_collect_constants
193
+ ret
194
+ end
195
+ end
175
196
 
176
197
  class ASTBody < ASTEntity
177
198
  def self.ripper_id; :bodystmt end
@@ -207,8 +228,9 @@ class ASTTopConstRef < ASTEntity
207
228
  def self.ripper_id; :top_const_ref end
208
229
  def collect_constants(result, context)
209
230
  context[:top] = true
210
- super(result, context)
231
+ ret = super(result, context)
211
232
  context[:top] = false
233
+ ret
212
234
  end
213
235
  end
214
236
 
@@ -239,6 +261,7 @@ class ASTConst < ASTEntity
239
261
  @const_name = args[0]
240
262
  end
241
263
  def collect_constants(result, context)
264
+ return super(result, context) if context[:stop_collect_constants]
242
265
  if context[:variable_assignment]
243
266
  result.declare_const(@const_name)
244
267
  else
@@ -262,7 +285,8 @@ class ASTConstPathRef < ASTEntity
262
285
  @const = ASTEntity.node_for(args.last)
263
286
  end
264
287
  def collect_constants(result, context)
265
- if context[:const_path_ref]
288
+ return super(result, context) if context[:stop_collect_constants]
289
+ if context[:const_path_ref] || context[:method_add_arg]
266
290
  r = TraversingResult.new
267
291
  c = context.dup
268
292
  c[:analyze_const] = false
@@ -280,6 +304,48 @@ class ASTConstPathRef < ASTEntity
280
304
  end
281
305
  end
282
306
 
307
+ class ASTMethodAddArg < ASTEntity
308
+ def self.ripper_id; :method_add_arg end
309
+ def initialize(*args)
310
+ @path = ASTEntity.node_for(args.first)
311
+ end
312
+
313
+ def collect_constants(result, context)
314
+ return super(result, context) if context[:stop_collect_constants]
315
+ if context[:method_add_arg]
316
+ r = TraversingResult.new
317
+ c = context.dup
318
+ c[:analyze_const] = false
319
+ path_consts = @path.collect_constants(r, context)
320
+ result.use_const(path_consts.used.join('::'), false)
321
+ else
322
+ r = TraversingResult.new
323
+ new_context = TraversingContext.new([], {method_add_arg: true, analyze_const: false})
324
+ path_consts = @path.collect_constants(r, new_context)
325
+ result.use_const(path_consts.used.join('::'))
326
+ end
327
+ result
328
+ end
329
+ end
330
+
331
+ class ASTDefs < ASTEntity
332
+ def self.ripper_id; :defs end
333
+ def collect_constants(result, context)
334
+ result
335
+ end
336
+ end
337
+
338
+ class ASTMethodAddBlock < ASTEntity
339
+ def self.ripper_id; :method_add_block end
340
+
341
+ def collect_constants(result, context)
342
+ context[:stop_collect_constants] = true
343
+ ret = super(result, context)
344
+ context[:stop_collect_constants] = nil
345
+ ret
346
+ end
347
+ end
348
+
283
349
  class ASTModule < ASTEntity
284
350
  def self.ripper_id; :module end
285
351
  def initialize(*args)
@@ -300,8 +366,9 @@ class ASTVarField < ASTEntity
300
366
  def self.ripper_id; :var_field end
301
367
  def collect_constants(result, context)
302
368
  context[:variable_assignment] = true
303
- super(result, context)
369
+ ret = super(result, context)
304
370
  context[:variable_assignment] = false
371
+ ret
305
372
  end
306
373
  end
307
374
 
@@ -313,6 +380,12 @@ class ASTRef < ASTEntity
313
380
  end
314
381
  end
315
382
 
383
+ class ASTLambda < ASTEntity
384
+ def self.ripper_id; :lambda end
385
+ def initialize(*args)
386
+ super(*(args[0..-2]))
387
+ end
388
+ end
316
389
 
317
390
  class Ripper
318
391
  def self.extract_constants(code)
@@ -1,10 +1,12 @@
1
1
  require 'spec_helper'
2
2
  require_relative '../../../lib/grape/reload/grape_api'
3
3
  describe Grape::Reload::AutoreloadInterceptor do
4
- let(:api_class) {
4
+ let!(:api_class) {
5
5
  nested_class = Class.new(Grape::API) do
6
- get :route do
7
- 'nested route'
6
+ namespace :nested do
7
+ get :route do
8
+ 'nested route'
9
+ end
8
10
  end
9
11
  end
10
12
 
@@ -18,7 +20,7 @@ describe Grape::Reload::AutoreloadInterceptor do
18
20
  }
19
21
 
20
22
  describe '.reinit!' do
21
- let(:app) {
23
+ let!(:app) {
22
24
  app = Rack::Builder.new
23
25
  app.run api_class
24
26
  app
@@ -31,10 +33,16 @@ describe Grape::Reload::AutoreloadInterceptor do
31
33
  get '/test_route'
32
34
  expect(last_response).to succeed
33
35
  expect(last_response.body).to eq('test')
36
+ get '/nested/nested/route'
37
+ expect(last_response).to succeed
38
+ expect(last_response.body).to eq('nested route')
34
39
  api_class.reinit!
35
40
  get '/test_route'
36
41
  expect(last_response).to succeed
37
42
  expect(last_response.body).to eq('test')
43
+ get '/nested/nested/route'
44
+ expect(last_response).to succeed
45
+ expect(last_response.body).to eq('nested route')
38
46
  end
39
47
  end
40
48
  end
@@ -42,9 +42,4 @@ describe Grape::Reload::DependencyMap do
42
42
 
43
43
  expect(dm.dependent_classes('file1')).to include('::Class2','::Class3')
44
44
  end
45
-
46
- it "raises error if dependencies can't be resolved" do
47
- allow(dm).to receive(:map).and_return(wrong_class_map)
48
- expect { dm.resolve_dependencies! }.to raise_error(Grape::Reload::UnresolvedDependenciesError)
49
- end
50
45
  end
@@ -11,6 +11,17 @@ describe Grape::RackBuilder do
11
11
  end
12
12
  end
13
13
  }
14
+ let(:middleware) {
15
+ Class.new do
16
+ def initialize(app)
17
+ @app = app
18
+ end
19
+ def call(env)
20
+ @app.call(env)
21
+ end
22
+ end
23
+ }
24
+
14
25
 
15
26
  before do
16
27
  builder.setup do
@@ -37,8 +48,16 @@ describe Grape::RackBuilder do
37
48
  end
38
49
  expect(config.mounts.size).to eq(2)
39
50
  end
51
+
52
+ it 'allows to add middleware' do
53
+ builder.setup do
54
+ use middleware do
55
+ end
56
+ end
57
+ expect(config.middleware.size).to eq(1)
58
+ end
40
59
  end
41
- #
60
+
42
61
  describe '.boot!' do
43
62
  before(:each) do
44
63
  builder.setup do
@@ -63,6 +82,7 @@ describe Grape::RackBuilder do
63
82
  describe '.application' do
64
83
  before(:each) do
65
84
  builder.setup do
85
+ use middleware
66
86
  mount 'Test::App1', to: '/test1'
67
87
  mount 'Test::App2', to: '/test2'
68
88
  end
@@ -72,6 +92,8 @@ describe Grape::RackBuilder do
72
92
  expect{ @app = builder.application }.not_to raise_error
73
93
  expect(@app).to be_an_instance_of(Rack::Builder)
74
94
  def @app.get_map; @map end
95
+ def @app.get_use; @use end
96
+ expect(@app.get_use.size).to eq(1)
75
97
  expect(@app.get_map.keys).to include('/test1','/test2')
76
98
  end
77
99
  end
@@ -2,8 +2,10 @@ require 'grape'
2
2
  require 'spec_helper'
3
3
 
4
4
  describe Grape::Reload::Watcher do
5
- def app; @app end
6
- before(:example) do
5
+ def app
6
+ @app
7
+ end
8
+ before(:each) do
7
9
  @app =
8
10
  Grape::RackBuilder.setup do
9
11
  add_source_path File.expand_path('**.rb', APP_ROOT)
@@ -31,6 +33,35 @@ describe Grape::Reload::Watcher do
31
33
  end
32
34
  end
33
35
 
36
+ describe 'force_reloading' do
37
+ before(:each) do
38
+ @app =
39
+ Grape::RackBuilder.setup do
40
+ add_source_path File.expand_path('**.rb', APP_ROOT)
41
+ add_source_path File.expand_path('**/*.rb', APP_ROOT)
42
+ environment 'test'
43
+ force_reloading true
44
+ reload_threshold 0
45
+ mount 'Test::App1', to: '/test1'
46
+ mount 'Test::App2', to: '/test2'
47
+ end.boot!.application
48
+ end
49
+
50
+ it 'reloads files within any environment with force_reloading options set' do
51
+ get '/test1/test'
52
+ expect(last_response).to succeed
53
+ expect(last_response.body).to eq('test1 response')
54
+
55
+ with_changed_fixture 'app1/test1.rb' do
56
+ get '/test1/test'
57
+ expect(last_response).to succeed
58
+ expect(last_response.body).to eq('test1 response changed')
59
+ end
60
+ end
61
+ end
62
+
63
+
64
+
34
65
  it 'reloads mounted app file' do
35
66
  get '/test1/mounted/test1'
36
67
  expect(last_response).to succeed
@@ -60,10 +60,71 @@ CODE
60
60
  def use_top_level
61
61
  TopLevel.new
62
62
  end
63
+ def self.method
64
+ SomeModule::ShouldntUse.call
65
+ end
66
+ end
67
+ CODE
68
+ }
69
+
70
+ let!(:deeply_nested) {
71
+ <<CODE
72
+ module Test
73
+ module Subtest
74
+ class App2 < Grape::API
75
+ end
76
+ end
63
77
  end
64
78
  CODE
65
79
  }
66
80
 
81
+ # Sequel-related
82
+ let!(:class_reference_with_call) {
83
+ <<CODE
84
+ module Test
85
+ module Subtest
86
+ class App2 < Grape::API(:test)
87
+ end
88
+ end
89
+ end
90
+ CODE
91
+ }
92
+
93
+ let!(:grape_desc_args) {
94
+ <<CODE
95
+ module Test
96
+ class App < Grape::API
97
+ group do
98
+ desc 'Blablabla',
99
+ entity: [Test::SomeAnotherEntity]
100
+ get :test do
101
+ SomeClass.usage
102
+ 'test2 response'
103
+ end
104
+ end
105
+ end
106
+ end
107
+ CODE
108
+ }
109
+
110
+ let!(:class_level_call_with_args) {
111
+ <<CODE
112
+ module Test
113
+ class TestClass
114
+ UseModule::UseClass.call(arg)
115
+ end
116
+ end
117
+ CODE
118
+ }
119
+
120
+ let!(:lambda_class_usage) {
121
+ <<CODE
122
+ some_method ->(arg) {
123
+ ModuleName::ClassName.call(arg)
124
+ }
125
+ CODE
126
+ }
127
+
67
128
  it 'extract consts from code1 correctly' do
68
129
  consts = Ripper.extract_constants(code1)
69
130
  expect(consts[:declared].flatten).to include(
@@ -81,11 +142,15 @@ CODE
81
142
  '::Test3::AnotherClass',
82
143
  '::Test::NotExists::Test1',
83
144
  '::SomeExternalClass',
84
- '::Superclass',
85
- '::SomeClass1',
86
- '::SomeClass2'
145
+ '::Superclass'
87
146
  )
88
147
 
148
+ expect(consts[:used].flatten).not_to include(
149
+ '::SomeClass1',
150
+ '::SomeClass2'
151
+ )
152
+
153
+
89
154
  end
90
155
  it 'extract consts from code2 correctly' do
91
156
  consts = Ripper.extract_constants(code2)
@@ -98,8 +163,37 @@ CODE
98
163
  '::Test::Mount2',
99
164
  '::Test::Mount10',
100
165
  '::Test::SomeAnotherEntity',
101
- '::SomeClass',
102
- '::TopLevel'
103
166
  )
167
+
168
+ expect(consts[:used].flatten).not_to include(
169
+ '::SomeClass',
170
+ '::TopLevel'
171
+ )
172
+
173
+ end
174
+
175
+ it 'extracts consts used in deeply nested modules up to root namespace' do
176
+ consts = Ripper.extract_constants(deeply_nested)
177
+ expect(consts[:used].flatten).to include('::Grape::API')
178
+ end
179
+
180
+ it 'extracts const with call (sequel-related)' do
181
+ consts = Ripper.extract_constants(class_reference_with_call)
182
+ expect(consts[:used].flatten).to include('::Grape::API')
183
+ end
184
+
185
+ it 'extracts consts from desc method args' do
186
+ consts = Ripper.extract_constants(grape_desc_args)
187
+ expect(consts[:used].flatten).to include('::Test::SomeAnotherEntity')
188
+ end
189
+
190
+ it 'does not mess up class name when class level method called with argument' do
191
+ consts = Ripper.extract_constants(class_level_call_with_args)
192
+ expect(consts[:used].flatten).to include('::UseModule::UseClass')
193
+ end
194
+
195
+ it 'does not include classes used in lambdas' do
196
+ consts = Ripper.extract_constants(lambda_class_usage)
197
+ expect(consts[:used].flatten).not_to include('::ModuleName::ClassName')
104
198
  end
105
199
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grape-reload
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - AMar4enko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-20 00:00:00.000000000 Z
11
+ date: 2014-10-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: grape
@@ -212,6 +212,7 @@ files:
212
212
  - lib/grape/reload.rb
213
213
  - lib/grape/reload/dependency_map.rb
214
214
  - lib/grape/reload/grape_api.rb
215
+ - lib/grape/reload/rack.rb
215
216
  - lib/grape/reload/rack_builder.rb
216
217
  - lib/grape/reload/storage.rb
217
218
  - lib/grape/reload/version.rb