middleman-apps 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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