grape-reload 0.0.3 → 0.0.4

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 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