middleman-apps 0.1.0 → 0.2.0

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.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.rubocop.yml +2 -1
  4. data/Gemfile +4 -0
  5. data/{LICENSE.txt → LICENSE} +0 -0
  6. data/README.md +166 -40
  7. data/features/e_asset_hash.feature +17 -0
  8. data/features/{directory_indexes.feature → e_directory_indexes.feature} +14 -16
  9. data/features/e_relative_assets.feature +17 -0
  10. data/features/f_code_reloading.feature +134 -0
  11. data/features/f_metadata.feature +25 -0
  12. data/features/{not_found_rack.feature → f_not_found.feature} +12 -22
  13. data/features/f_options.feature +39 -0
  14. data/features/step_definitions/capybara_steps.rb +18 -0
  15. data/features/step_definitions/mechanize_steps.rb +19 -0
  16. data/features/step_definitions/rack_app_steps.rb +4 -2
  17. data/features/support/aruba.rb +22 -0
  18. data/features/support/env.rb +0 -14
  19. data/features/support/helpers.rb +11 -0
  20. data/features/support/mechanize.rb +32 -0
  21. data/features/support/poltegeist.rb +35 -0
  22. data/features/v_activating.feature +74 -0
  23. data/features/v_building.feature +31 -0
  24. data/features/v_inheriting_from.feature +38 -0
  25. data/fixtures/asset_hash/apps/ignored_app.rb +10 -0
  26. data/fixtures/asset_hash/apps/test_app.rb +11 -0
  27. data/fixtures/asset_hash/config.rb +2 -0
  28. data/fixtures/asset_hash/source/error.html.erb +2 -0
  29. data/fixtures/{complex-app → asset_hash}/source/index.html.erb +0 -0
  30. data/fixtures/asset_hash/source/layouts/layout.erb +12 -0
  31. data/fixtures/asset_hash/source/layouts/page.erb +6 -0
  32. data/fixtures/asset_hash/source/stylesheets/style.css.scss.erb +5 -0
  33. data/fixtures/dir_index/apps/ignored_app.rb +10 -0
  34. data/fixtures/dir_index/apps/test_app.rb +10 -0
  35. data/fixtures/dir_index/config.rb +2 -0
  36. data/fixtures/{simple-app → dir_index}/source/index.html.erb +0 -0
  37. data/fixtures/mount_path/apps/test_app.rb +10 -0
  38. data/fixtures/mount_path/config.rb +1 -0
  39. data/fixtures/{complex-app/build/index.html → mount_path/source/index.html.erb} +0 -0
  40. data/fixtures/real_world/apps/awesome_api.rb +40 -0
  41. data/fixtures/{complex-app → real_world}/apps/child_app.rb +15 -2
  42. data/fixtures/{complex-app/apps/test_app.rb → real_world/apps/ignored_app.rb} +2 -2
  43. data/fixtures/real_world/apps/no_namespace.rb +8 -0
  44. data/fixtures/real_world/apps/test_app.rb +16 -0
  45. data/fixtures/real_world/config.rb +13 -0
  46. data/fixtures/real_world/source/apps.html.erb +14 -0
  47. data/fixtures/real_world/source/custom.html.erb +1 -0
  48. data/fixtures/{simple-app/build/index.html → real_world/source/index.html.erb} +0 -0
  49. data/fixtures/real_world/source/layouts/_partial.erb +1 -0
  50. data/fixtures/real_world/source/layouts/layout.erb +11 -0
  51. data/fixtures/real_world/source/layouts/page.erb +6 -0
  52. data/fixtures/real_world/source/layouts/test.html.markdown.erb +3 -0
  53. data/fixtures/relative_assets/apps/ignored_app.rb +10 -0
  54. data/fixtures/relative_assets/apps/test_app.rb +11 -0
  55. data/fixtures/relative_assets/config.rb +2 -0
  56. data/fixtures/relative_assets/source/error.html.erb +2 -0
  57. data/fixtures/relative_assets/source/index.html.erb +2 -0
  58. data/fixtures/relative_assets/source/layouts/layout.erb +12 -0
  59. data/fixtures/relative_assets/source/layouts/page.erb +6 -0
  60. data/fixtures/relative_assets/source/stylesheets/style.css.scss.erb +5 -0
  61. data/fixtures/simple/apps/ignored_app.rb +10 -0
  62. data/fixtures/simple/apps/test_app.rb +10 -0
  63. data/fixtures/simple/config.rb +1 -0
  64. data/fixtures/simple/source/index.html.erb +2 -0
  65. data/lib/middleman/apps.rb +26 -7
  66. data/lib/middleman/apps/base.rb +71 -15
  67. data/lib/middleman/apps/extension.rb +34 -158
  68. data/lib/middleman/apps/version.rb +2 -1
  69. data/lib/middleman/sitemap/app_collection.rb +225 -0
  70. data/lib/middleman/sitemap/app_resource.rb +61 -0
  71. metadata +77 -31
  72. data/features/activation.feature +0 -53
  73. data/features/build.feature +0 -20
  74. data/features/child_app.feature +0 -18
  75. data/features/complex_app.feature +0 -66
  76. data/features/not_found_server.feature +0 -8
  77. data/features/verbose.feature +0 -23
  78. data/fixtures/complex-app/apps/awesome_api.rb +0 -11
  79. data/fixtures/complex-app/build/error.html +0 -1
  80. data/fixtures/complex-app/config.rb +0 -1
  81. data/fixtures/simple-app/apps/test_app.rb +0 -8
  82. data/fixtures/simple-app/build/error.html +0 -1
  83. data/fixtures/simple-app/config.rb +0 -1
@@ -1,35 +1,91 @@
1
1
  require 'sinatra'
2
+ require 'middleman/apps'
2
3
 
3
4
  module Middleman
4
5
  module Apps
5
6
  # Base application class for creating child applications.
6
7
  #
7
- # Inheriting from this class should provide better syncronization with the
8
- # static middleman app.
8
+ # Inheriting from this class provides better integration with the static
9
+ # middleman app.
9
10
  #
10
11
  class Base < ::Sinatra::Base
11
- # set :static, true
12
+ # @!attribute [r] static
13
+ # Serve static assets from #public_folder, if found
14
+ set :static, true
12
15
 
13
- # set :mm_root, File.dirname(File.dirname(__FILE__))
14
- # set :mm_dir, settings.development? ? 'source' : 'build'
15
- # set :public_folder, File.join(settings.mm_root, settings.mm_dir)
16
- # set :views, settings.public_folder
16
+ set :environment, (ENV['RACK_ENV'] || 'development').to_sym
17
+
18
+ # @!attribute [r] mm_app
19
+ # Middleman Application instance for references to config, sitemap, etc.
20
+ #
21
+ # Lazily evaluated since this is a bit costly.
22
+ #
23
+ set :mm_app, Middleman::Apps.middleman_app
24
+
25
+ # @!attribute [r] views
26
+ # Path to the directory containing our layout files.
27
+ set :views, File.join(settings.mm_app.root, 'source', 'layouts')
28
+
29
+ configure :production do
30
+ # @!attribute [r] public_folder
31
+ # Path to the directory containing our layout files.
32
+ set :public_folder, File.join(settings.mm_app.root, 'build')
33
+ end
34
+
35
+ configure :development do
36
+ set :show_exceptions, true
37
+ set :public_folder, File.join(settings.mm_app.root, 'source')
38
+ end
17
39
 
18
40
  not_found do
19
- send_file path_for_not_found, status: 404
41
+ status 404
42
+ Middleman::Apps.not_found(settings.mm_app)
43
+ end
44
+
45
+ def self.app_resource
46
+ Middleman::Apps.find_app_resource_for(self, settings.mm_app)
47
+ end
48
+
49
+ def self.metadata
50
+ res = app_resource
51
+ data = res ? res.locals : {}
52
+ keys = %i[routes html_description]
53
+ data = keys.map { |key| [key, res.send(key)] }.to_h.merge(data) if res
54
+ Hashie::Mash.new(data)
55
+ rescue NameError
56
+ data
57
+ end
58
+
59
+ def self.set_metadata(key, val, overwrite: false)
60
+ return if !overwrite && settings.respond_to?("app_#{key}")
61
+ set "app_#{key}", val
62
+ app_resource && app_resource.update_locals(key, val)
63
+ end
64
+
65
+ def self.add_routes_to_metadata(*verbs)
66
+ verbs = %i[get post put patch delete] if verbs.empty?
67
+ app_routes = verbs.map do |verb|
68
+ (routes[verb.to_s.upcase] || []).map do |route|
69
+ "##{verb.to_s.upcase} #{route[0]}"
70
+ end
71
+ end.flatten
72
+ set_metadata :routes, app_routes, overwrite: true
20
73
  end
21
74
 
22
75
  protected
23
76
 
24
- def middleman_app
25
- Middleman::Apps.middleman_app
77
+ def metadata
78
+ self.class.metadata
26
79
  end
27
80
 
28
- def path_for_not_found
29
- Middleman::Apps.within_extension do
30
- path = find_resource(options.not_found)
31
- app.root_path.join(app.config.build_dir, path).to_s
32
- end
81
+ # Render a MM layout with the given name.
82
+ #
83
+ def middleman_layout(name, opts = {})
84
+ locs = opts.delete(:locals) || {}
85
+ opts[:layout] ||= name
86
+
87
+ res = self.class.app_resource
88
+ res.render(opts, res.locals.merge(locs))
33
89
  end
34
90
  end
35
91
  end
@@ -1,3 +1,4 @@
1
+ require 'forwardable'
1
2
  require 'middleman-core'
2
3
  require 'middleman-core/load_paths'
3
4
 
@@ -11,6 +12,10 @@ module Middleman
11
12
  # Usage examples can be seen in README for this extension.
12
13
  #
13
14
  class Extension < ::Middleman::Extension
15
+ extend Forwardable
16
+ attr_reader :collection
17
+ def_delegators :@collection, :apps_list
18
+ expose_to_template :apps_list
14
19
 
15
20
  # @!group Options for Extension
16
21
 
@@ -22,6 +27,9 @@ module Middleman
22
27
  option :namespace, nil, 'Namespace for the child apps'
23
28
  option :map, {}, 'Mappings for differently named child apps'
24
29
  option :verbose, false, 'Displays list of child apps that were ignored'
30
+ option :mount_path, '/', 'Root mount path for child apps'
31
+ option :app_dir, ENV['MM_APPS_DIR'] || 'apps',
32
+ 'The directory child apps are stored in'
25
33
 
26
34
  # @!endgroup
27
35
 
@@ -29,61 +37,11 @@ module Middleman
29
37
  super
30
38
  # useful for converting file names to ruby classes
31
39
  require 'active_support/core_ext/string/inflections'
32
- end
33
-
34
- # Mount all child apps on a specific Rack app (or current app)
35
- #
36
- # @param [Rack::App] rack_app app on which to mount child apps
37
- # Default: app from MM configuration
38
- #
39
- # @return [Rack::App] rack_app with child apps mounted on top
40
- #
41
- def mount_child_apps(rack_app = nil)
42
- rack_app ||= app
43
- child_apps.each do |url, klass|
44
- rack_app.map(url) { run klass }
45
- end
46
- rack_app
47
- end
48
-
49
- # Get a hash of all child applications URLs paths matched to corresponding
50
- # Ruby classes.
51
- #
52
- # Warning is raised (if `verbose` option is `true`) when a child app was
53
- # found, but could not be mapped due to the specified config.
54
- #
55
- # @return [Hash] - child application URL vs Ruby class
56
- #
57
- def child_apps
58
- apps_list.map do |mapp|
59
- require mapp
60
- klass = get_application_class_for(mapp)
61
- warn "Ignored child app: #{mapp}" unless klass
62
- [get_application_url_for(mapp), klass] if klass
63
- end.compact.to_h
64
- end
65
-
66
- # Get a Rack::App that can serve the MM app's build directory.
67
- #
68
- # Directory paths, and 404 error page are deduced from extensions' options.
69
- #
70
- # @return [Rack::App] Rack::TryStatic app for MM app's build directory.
71
- #
72
- def middleman_static_app
73
- not_found = options.not_found
74
- return create_static_app(root) unless not_found
75
-
76
- not_found_path = File.join(build_dir, find_resource(not_found))
77
- create_static_app build_dir, not_found_path
78
- end
40
+ require 'middleman/sitemap/app_resource'
41
+ require 'middleman/sitemap/app_collection'
79
42
 
80
- # Get a list of all child apps that are found in `MM_ROOT/apps` directory.
81
- #
82
- def apps_list
83
- pattern = File.join(app.root, 'apps', '*.rb')
84
- Dir[pattern].map do |file|
85
- File.realpath(file) if File.file?(file)
86
- end.compact
43
+ # get a reference to all the apps
44
+ @collection = Sitemap::AppCollection.new(app, self, options)
87
45
  end
88
46
 
89
47
  # Run `after_configuration` hook passed on by MM
@@ -99,8 +57,27 @@ module Middleman
99
57
  #
100
58
  def after_configuration
101
59
  create_config_ru
60
+ return if app.build?
61
+
62
+ app.sitemap.register_resource_list_manipulator(:child_apps, @collection)
102
63
  return unless app.server?
103
- mount_child_apps(app)
64
+
65
+ watch_child_apps
66
+ @collection.mount_child_apps(app)
67
+ end
68
+
69
+ # Set a watcher to reload MM when files change in the directory for the
70
+ # child apps.
71
+ #
72
+ # @return [nil]
73
+ #
74
+ def watch_child_apps
75
+ # Make sure it exists, or `listen` will explode.
76
+ app_path = File.expand_path(options.app_dir, app.root)
77
+ ::FileUtils.mkdir_p(app_path)
78
+ watcher = app.files.watch :reload, path: app_path, only: /\.rb$/
79
+ list = @collection
80
+ watcher.on_change { list.mount_child_apps(app) }
104
81
  end
105
82
 
106
83
  # Create a `config.ru` file, if one does not exist, yet.
@@ -117,115 +94,14 @@ module Middleman
117
94
  path = File.join(app.root, 'config.ru')
118
95
  return if File.exist?(path)
119
96
 
120
- content = <<-CONTENT.gsub(/^ {6}/, '')
121
- ENV['RACK_ENV'] = 'production'
97
+ content = <<-CONTENT.gsub(/^ {8}/, '')
98
+ ENV['RACK_ENV'] ||= 'production'
122
99
  require 'middleman/apps'
123
100
  run Middleman::Apps.rack_app
124
101
  CONTENT
125
102
 
126
103
  File.open(path, 'wb') { |file| file.puts content }
127
104
  end
128
-
129
- # Create a Rack::TryStatic application for the given directory root.
130
- #
131
- # @param [String] root - path to directory root
132
- # @param [String] path - path to not found error page
133
- # If not provided, default 404 response from Rack
134
- # is served.
135
- #
136
- # @return [Rack::App] static app for the `root` directory
137
- #
138
- # @api private
139
- #
140
- def create_static_app(root, path = nil)
141
- unless File.exist?(path)
142
- warn("Could not find: #{path}")
143
- path = nil
144
- end
145
-
146
- # require 'rack/contrib'
147
- require 'middleman/apps/rack_contrib'
148
- ::Rack::Builder.new do
149
- use ::Rack::TryStatic, urls: ['/'], root: root,
150
- try: ['.html', 'index.html', '/index.html']
151
- run ::Rack::NotFound.new(path)
152
- end
153
- end
154
-
155
- # Find a resource given its path, destination path, or page_id.
156
- #
157
- # @param [String] name - identifier for this resource
158
- # @return [String] relative path to resource
159
- #
160
- # @api private
161
- #
162
- def find_resource(name)
163
- sitemap = app.sitemap
164
- resource = sitemap.find_resource_by_path(name)
165
- resource ||= sitemap.find_resource_by_destination_path(name)
166
- resource ||= sitemap.find_resource_by_page_id(name)
167
- resource ? resource.destination_path : name
168
- end
169
-
170
- private
171
-
172
- # Warn user about message if `verbose` option is on.
173
- #
174
- # @param [String] message - message to display
175
- #
176
- # @private
177
- # @api private
178
- #
179
- def warn(message)
180
- logger.warn(message) if logger && options.verbose
181
- end
182
-
183
- # Get path to MM's build dir.
184
- #
185
- # @return [String] path to build dir
186
- #
187
- def build_dir
188
- File.expand_path(app.config.build_dir.to_s)
189
- end
190
-
191
- # Convert options data to a hash for easy searches.
192
- #
193
- # @api private
194
- # @return [Hash] options data
195
- #
196
- def mappings
197
- options.map.map { |key, val| [key.to_s, val] }.to_h
198
- end
199
-
200
- # Get URL at which given child app should be mounted.
201
- #
202
- # @api private
203
- # @param [String] file - path to child app
204
- # @return [String] url component for the child app
205
- #
206
- def get_application_url_for(file)
207
- name = File.basename(file, '.rb')
208
- url = mappings[name]
209
- url = url[:url] if url.is_a?(Hash)
210
- '/' + (url ? url.to_s.gsub(%r{^\/}, '') : name.titleize.parameterize)
211
- end
212
-
213
- # Get Application Class for the child app.
214
- #
215
- # @api private
216
- # @param [String] file - path to child app
217
- # @return [Class, nil] Class for the child app, if exists.
218
- #
219
- def get_application_class_for(file)
220
- name = File.basename(file, '.rb')
221
- namespace = options.namespace
222
-
223
- klass = mappings[name][:class] if mappings[name].is_a?(Hash)
224
- klass ||= namespace ? "#{namespace}/#{name}" : name
225
- klass.to_s.classify.constantize
226
- rescue NameError
227
- return nil
228
- end
229
105
  end
230
106
  end
231
107
  end
@@ -1,5 +1,6 @@
1
1
  module Middleman
2
2
  module Apps
3
- VERSION = '0.1.0'.freeze
3
+ # current version for this gem
4
+ VERSION = '0.2.0'.freeze
4
5
  end
5
6
  end
@@ -0,0 +1,225 @@
1
+ module Middleman
2
+ module Sitemap
3
+ # Resource manipulator class that handles list of all our child app
4
+ # resources.
5
+ #
6
+ # To evaluate anything in the context of an instance of this class, use:
7
+ #
8
+ # Middleman::Apps.with_app_list do
9
+ # apps_list # => this will be returned by the block
10
+ # end
11
+ #
12
+ class AppCollection
13
+ def initialize(app, _extension, options = {})
14
+ @app = app
15
+ @options = options
16
+ @sitemap = app.sitemap
17
+ @app_dir = app.root_path.join(options.app_dir)
18
+ end
19
+
20
+ # Add our child apps to the list of resources managed by MM.
21
+ #
22
+ # @param [Array<Middleman::Sitemap::Resource>] resources - resource list
23
+ # @return [Array<Middleman::Sitemap::Resource>] updated resource list
24
+ #
25
+ def manipulate_resource_list(resources)
26
+ resources + apps_list
27
+ end
28
+
29
+ # Get a list of all child app resources found in child apps directory.
30
+ #
31
+ # All child apps will be reloaded everytime this method is called.
32
+ #
33
+ # @return [Array<Middleman::Sitemap::AppResource>] array of child apps
34
+ #
35
+ def apps_list
36
+ Dir[@app_dir.join('*.rb').to_s].map do |file|
37
+ path = Pathname.new(file)
38
+ resource = create_app(path) if path.file?
39
+ warn "Ignored child app: #{path}" unless resource
40
+ resource
41
+ end.compact
42
+ end
43
+
44
+ # Create or reload child app resource for each found child app.
45
+ #
46
+ # @param [String] path to the child app source file
47
+ # @return [Middleman::Sitemap::AppResource] app resource
48
+ #
49
+ def create_app(path)
50
+ reload_resource_at path
51
+ url = get_application_url_for(path)
52
+ klass = get_application_class_for(path)
53
+ return unless klass
54
+
55
+ source = get_source_file(path, @app_dir, :app)
56
+ title = (klass || url).to_s.titleize
57
+ AppResource.new(@sitemap, url.gsub(%r{^\/}, ''), source).tap do |p|
58
+ p.add_metadata locals: { url: url, klass: klass, title: title }
59
+ end
60
+ end
61
+
62
+ # Mount all child apps on a specific Rack app (or current app)
63
+ #
64
+ # Warning is raised (if `verbose` option is `true`) when a child app was
65
+ # found, but could not be mapped due to the specified config.
66
+ #
67
+ # @param [Rack::App] rack_app app on which to mount child apps
68
+ # Default: app from MM configuration
69
+ #
70
+ # @return [Rack::App] rack_app with child apps mounted on top
71
+ #
72
+ def mount_child_apps(rack_app = nil)
73
+ rack_app ||= @app
74
+ apps_list.each do |res|
75
+ rack_app.map(res.url) { run res.klass } if res.klass
76
+ end
77
+ rack_app
78
+ end
79
+
80
+ # Get a Rack::App that can serve the MM app's build directory.
81
+ #
82
+ # Directory paths, and 404 error page are deduced from extensions'
83
+ # options.
84
+ #
85
+ # @return [Rack::App] Rack::TryStatic app for MM app's build directory.
86
+ #
87
+ def middleman_static_app
88
+ not_found = @options.not_found
89
+ return create_static_app(build_dir) unless not_found
90
+
91
+ not_found_path = File.join(build_dir, find_resource(not_found))
92
+ create_static_app build_dir, not_found_path
93
+ end
94
+
95
+ protected
96
+
97
+ # Create a Rack::TryStatic application for the given directory root.
98
+ #
99
+ # @param [String] root - path to directory root
100
+ # @param [String] path - path to not found error page
101
+ # If not provided, default 404 response from Rack
102
+ # is served.
103
+ #
104
+ # @return [Rack::App] static app for the `root` directory
105
+ #
106
+ # @api private
107
+ #
108
+ def create_static_app(root, path = nil)
109
+ unless File.exist?(path)
110
+ warn("Could not find: #{path}")
111
+ path = nil
112
+ end
113
+
114
+ # require 'rack/contrib'
115
+ require 'middleman/apps/rack_contrib'
116
+ ::Rack::Builder.new do
117
+ use ::Rack::TryStatic, urls: ['/'], root: root,
118
+ try: ['.html', 'index.html', '/index.html']
119
+ run ::Rack::NotFound.new(path)
120
+ end
121
+ end
122
+
123
+ # Find a resource given its path, destination path, or page_id.
124
+ #
125
+ # @param [String] name - identifier for this resource
126
+ # @return [String] relative path to resource
127
+ #
128
+ # @api private
129
+ #
130
+ def find_resource(name)
131
+ sitemap = @app.sitemap
132
+ resource = sitemap.find_resource_by_path(name)
133
+ resource ||= sitemap.find_resource_by_destination_path(name)
134
+ resource ||= sitemap.find_resource_by_page_id(name)
135
+ resource ? resource.destination_path : name
136
+ end
137
+
138
+ private
139
+
140
+ # Warn user about message if `verbose` option is on.
141
+ #
142
+ # @param [String] message - message to display
143
+ #
144
+ # @private
145
+ # @api private
146
+ #
147
+ def warn(message)
148
+ @app.logger.warn(message) if @app.logger && @options.verbose
149
+ end
150
+
151
+ # Get path to MM's build dir.
152
+ #
153
+ # @return [String] path to build dir
154
+ #
155
+ def build_dir
156
+ @app.root_path.join(@app.config.build_dir.to_s)
157
+ end
158
+
159
+ # Convert options data to a hash for easy searches.
160
+ #
161
+ # @api private
162
+ # @return [Hash] options data
163
+ #
164
+ def mappings
165
+ @options.map.map { |key, val| [key.to_s, val] }.to_h
166
+ end
167
+
168
+ # Get URL at which given child app should be mounted.
169
+ #
170
+ # @api private
171
+ # @param [String] path - path to child app
172
+ # @return [String] url component for the child app
173
+ #
174
+ def get_application_url_for(path)
175
+ name = path.basename('.rb').to_s
176
+ url = mappings[name]
177
+ url = url[:url] if url.is_a?(Hash)
178
+ url ||= name.to_s.titleize.parameterize
179
+ "#{@options.mount_path}/#{url}".gsub(%r{\/+}, '/')
180
+ end
181
+
182
+ # Get Application Class for the child app.
183
+ #
184
+ # @api private
185
+ # @param [String] path - path to child app
186
+ # @return [Class, nil] Class for the child app, if exists.
187
+ #
188
+ def get_application_class_for(path)
189
+ name = path.basename('.rb').to_s
190
+ namespace = @options.namespace
191
+
192
+ klass = mappings[name][:class] if mappings[name].is_a?(Hash)
193
+ klass ||= namespace ? "#{namespace}/#{name}" : name
194
+ klass.to_s.classify.safe_constantize
195
+ end
196
+
197
+ # Get SourceFile instance from the given path.
198
+ #
199
+ # @api private
200
+ # @param [String] path - path to the source file
201
+ # @return [Middleman::SourceFile]
202
+ def get_source_file(path, dir, name)
203
+ ::Middleman::SourceFile.new(path.relative_path_from(dir),
204
+ path, path.dirname.to_s, Set.new([name]), 0)
205
+ end
206
+
207
+ # Reload resource at a given file path
208
+ #
209
+ # @api private
210
+ # @param [String] path - path to the source file
211
+ # @return [nil]
212
+ #
213
+ def reload_resource_at(path)
214
+ if $LOADED_FEATURES.include?(path.to_s)
215
+ klass = get_application_class_for(path)
216
+ container = klass.to_s.deconstantize.safe_constantize || Object
217
+ container.send(:remove_const, klass.to_s.demodulize) if klass
218
+ $LOADED_FEATURES.delete(path.to_s)
219
+ end
220
+
221
+ require path
222
+ end
223
+ end
224
+ end
225
+ end