middleman-core 3.0.14 → 3.1.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. data/features/clean_build.feature +4 -5
  2. data/features/content_type.feature +43 -0
  3. data/features/data.feature +9 -4
  4. data/features/front-matter-neighbor.feature +151 -0
  5. data/features/queryable.feature +31 -0
  6. data/features/step_definitions/queryable_steps.rb +123 -0
  7. data/fixtures/content-type-app/config.rb +1 -0
  8. data/fixtures/content-type-app/source/.htaccess +1 -0
  9. data/fixtures/content-type-app/source/README +1 -0
  10. data/fixtures/content-type-app/source/images/blank.gif +0 -0
  11. data/fixtures/content-type-app/source/index.html +1 -0
  12. data/fixtures/content-type-app/source/javascripts/app.js +1 -0
  13. data/fixtures/content-type-app/source/override.html +5 -0
  14. data/fixtures/content-type-app/source/stylesheets/site.css +1 -0
  15. data/fixtures/frontmatter-neighbor-app/config.rb +0 -0
  16. data/fixtures/frontmatter-neighbor-app/source/front-matter-2.php.erb +2 -0
  17. data/fixtures/frontmatter-neighbor-app/source/front-matter-2.php.erb.frontmatter +4 -0
  18. data/fixtures/frontmatter-neighbor-app/source/front-matter-auto.erb +1 -0
  19. data/fixtures/frontmatter-neighbor-app/source/front-matter-auto.erb.frontmatter +4 -0
  20. data/fixtures/frontmatter-neighbor-app/source/front-matter-change.html.erb +1 -0
  21. data/fixtures/frontmatter-neighbor-app/source/front-matter-change.html.erb.frontmatter +4 -0
  22. data/fixtures/frontmatter-neighbor-app/source/front-matter-encoding.html.erb +1 -0
  23. data/fixtures/frontmatter-neighbor-app/source/front-matter-encoding.html.erb.frontmatter +5 -0
  24. data/fixtures/frontmatter-neighbor-app/source/json-front-matter-2.php.erb +2 -0
  25. data/fixtures/frontmatter-neighbor-app/source/json-front-matter-2.php.erb.frontmatter +4 -0
  26. data/fixtures/frontmatter-neighbor-app/source/json-front-matter-auto.erb +1 -0
  27. data/fixtures/frontmatter-neighbor-app/source/json-front-matter-auto.erb.frontmatter +4 -0
  28. data/fixtures/frontmatter-neighbor-app/source/json-front-matter.html.erb +1 -0
  29. data/fixtures/frontmatter-neighbor-app/source/json-front-matter.html.erb.frontmatter +4 -0
  30. data/fixtures/frontmatter-neighbor-app/source/raw-front-matter.html +1 -0
  31. data/fixtures/frontmatter-neighbor-app/source/raw-front-matter.html.frontmatter +4 -0
  32. data/fixtures/frontmatter-neighbor-app/source/raw-front-matter.php +1 -0
  33. data/fixtures/frontmatter-neighbor-app/source/raw-front-matter.php.frontmatter +4 -0
  34. data/fixtures/frontmatter-settings-neighbor-app/config.rb +4 -0
  35. data/fixtures/frontmatter-settings-neighbor-app/source/alternate_layout.html.erb +1 -0
  36. data/fixtures/frontmatter-settings-neighbor-app/source/alternate_layout.html.erb.frontmatter +3 -0
  37. data/fixtures/frontmatter-settings-neighbor-app/source/ignored.html.erb +1 -0
  38. data/fixtures/frontmatter-settings-neighbor-app/source/ignored.html.erb.frontmatter +3 -0
  39. data/fixtures/frontmatter-settings-neighbor-app/source/layouts/alternate.erb +3 -0
  40. data/fixtures/frontmatter-settings-neighbor-app/source/layouts/override.erb +2 -0
  41. data/fixtures/frontmatter-settings-neighbor-app/source/override_layout.html.erb +1 -0
  42. data/fixtures/frontmatter-settings-neighbor-app/source/override_layout.html.erb.frontmatter +3 -0
  43. data/fixtures/frontmatter-settings-neighbor-app/source/page_mentioned.html.erb +1 -0
  44. data/fixtures/frontmatter-settings-neighbor-app/source/page_mentioned.html.erb.frontmatter +3 -0
  45. data/fixtures/nested-data-app/config.rb +1 -0
  46. data/fixtures/nested-data-app/data/examples/test.yml +1 -0
  47. data/fixtures/nested-data-app/source/test.html.erb +1 -0
  48. data/fixtures/queryable-app/config.rb +0 -0
  49. data/fixtures/queryable-app/source/2010-08-08-test-document-file.html.markdown +8 -0
  50. data/fixtures/queryable-app/source/2010-08-09-another-test-document.html.markdown +10 -0
  51. data/fixtures/queryable-app/source/2011-12-26-some-test-document.html.markdown +6 -0
  52. data/fixtures/queryable-app/source/document_with_date_in_yaml.html.markdown +7 -0
  53. data/fixtures/queryable-app/source/document_without_date.html.markdown +7 -0
  54. data/lib/middleman-core/application.rb +52 -84
  55. data/lib/middleman-core/cli.rb +3 -2
  56. data/lib/middleman-core/cli/build.rb +5 -7
  57. data/lib/middleman-core/cli/bundler.rb +1 -1
  58. data/lib/middleman-core/cli/console.rb +46 -0
  59. data/lib/middleman-core/configuration.rb +235 -0
  60. data/lib/middleman-core/core_extensions.rb +0 -6
  61. data/lib/middleman-core/core_extensions/data.rb +34 -11
  62. data/lib/middleman-core/core_extensions/extensions.rb +31 -35
  63. data/lib/middleman-core/core_extensions/external_helpers.rb +7 -7
  64. data/lib/middleman-core/core_extensions/file_watcher.rb +1 -1
  65. data/lib/middleman-core/core_extensions/front_matter.rb +44 -38
  66. data/lib/middleman-core/core_extensions/rendering.rb +27 -21
  67. data/lib/middleman-core/core_extensions/request.rb +41 -73
  68. data/lib/middleman-core/core_extensions/routing.rb +7 -24
  69. data/lib/middleman-core/core_extensions/show_exceptions.rb +6 -8
  70. data/lib/middleman-core/extensions.rb +35 -0
  71. data/lib/middleman-core/logger.rb +9 -0
  72. data/lib/middleman-core/meta_pages.rb +93 -0
  73. data/lib/middleman-core/meta_pages/assets/config.css +36 -0
  74. data/lib/middleman-core/meta_pages/assets/glyphicons-halflings.png +0 -0
  75. data/lib/middleman-core/meta_pages/assets/jquery-1.8.2.min.js +2 -0
  76. data/lib/middleman-core/meta_pages/assets/jquery.details-1.6.min.js +6 -0
  77. data/lib/middleman-core/meta_pages/assets/meta.css +368 -0
  78. data/lib/middleman-core/meta_pages/assets/sitemap.css +635 -0
  79. data/lib/middleman-core/meta_pages/assets/sitemap.js +0 -0
  80. data/lib/middleman-core/meta_pages/config_setting.rb +39 -0
  81. data/lib/middleman-core/meta_pages/sitemap_resource.rb +52 -0
  82. data/lib/middleman-core/meta_pages/sitemap_tree.rb +73 -0
  83. data/lib/middleman-core/meta_pages/templates/config.html.erb +59 -0
  84. data/lib/middleman-core/meta_pages/templates/index.html.erb +21 -0
  85. data/lib/middleman-core/meta_pages/templates/sitemap.html.erb +31 -0
  86. data/lib/middleman-core/preview_server.rb +18 -7
  87. data/lib/middleman-core/profiling.rb +1 -1
  88. data/lib/middleman-core/renderers/erb.rb +5 -5
  89. data/lib/middleman-core/renderers/less.rb +3 -3
  90. data/lib/middleman-core/renderers/markdown.rb +10 -9
  91. data/lib/middleman-core/renderers/redcarpet.rb +16 -21
  92. data/lib/middleman-core/renderers/sass.rb +4 -5
  93. data/lib/middleman-core/renderers/slim.rb +4 -1
  94. data/lib/middleman-core/sitemap.rb +3 -3
  95. data/lib/middleman-core/sitemap/extensions/content_type.rb +16 -0
  96. data/lib/middleman-core/sitemap/extensions/on_disk.rb +2 -2
  97. data/lib/middleman-core/sitemap/extensions/proxies.rb +26 -16
  98. data/lib/middleman-core/sitemap/queryable.rb +148 -0
  99. data/lib/middleman-core/sitemap/resource.rb +14 -2
  100. data/lib/middleman-core/sitemap/store.rb +8 -7
  101. data/lib/middleman-core/step_definitions/builder_steps.rb +3 -0
  102. data/lib/middleman-core/step_definitions/server_steps.rb +6 -1
  103. data/lib/middleman-core/templates/default/source/layouts/layout.erb +1 -1
  104. data/lib/middleman-core/templates/extension/lib/lib.rb +17 -26
  105. data/lib/middleman-core/templates/html5/source/layouts/layout.erb +1 -1
  106. data/lib/middleman-core/templates/shared/Gemfile.tt +6 -1
  107. data/lib/middleman-core/util.rb +7 -5
  108. data/lib/middleman-core/version.rb +1 -1
  109. data/middleman-core.gemspec +1 -1
  110. metadata +126 -28
  111. data/lib/middleman-core/core_extensions/builder.rb +0 -17
  112. data/lib/middleman-core/core_extensions/ruby_encoding.rb +0 -25
  113. data/middleman-core-x86-mingw32.gemspec +0 -38
@@ -39,8 +39,8 @@ module Middleman
39
39
  klass.start(["-h", task].compact, :shell => self.shell)
40
40
  else
41
41
  list = []
42
- Thor::Util.thor_classes_in(Middleman::Cli).each do |klass|
43
- list += klass.printable_tasks(false)
42
+ Thor::Util.thor_classes_in(Middleman::Cli).each do |thor_class|
43
+ list += thor_class.printable_tasks(false)
44
44
  end
45
45
  list.sort!{ |a,b| a[0] <=> b[0] }
46
46
 
@@ -87,3 +87,4 @@ require "middleman-core/cli/bundler"
87
87
  require "middleman-core/cli/extension"
88
88
  require "middleman-core/cli/server"
89
89
  require "middleman-core/cli/build"
90
+ require "middleman-core/cli/console"
@@ -17,9 +17,8 @@ module Middleman::Cli
17
17
  desc "build [options]", "Builds the static site for deployment"
18
18
  method_option :clean,
19
19
  :type => :boolean,
20
- :aliases => "-c",
21
- :default => false,
22
- :desc => 'Removes orphaned files or directories from build'
20
+ :default => true,
21
+ :desc => 'Remove orphaned files from build (--no-clean to disable)'
23
22
  method_option :glob,
24
23
  :type => :string,
25
24
  :aliases => "-g",
@@ -60,13 +59,12 @@ module Middleman::Cli
60
59
 
61
60
  opts = {}
62
61
  opts[:glob] = options["glob"] if options.has_key?("glob")
63
- opts[:clean] = options["clean"] if options.has_key?("clean")
62
+ opts[:clean] = options["clean"] if options.has_key?("clean")
64
63
 
65
64
  action GlobAction.new(self, opts)
66
65
 
67
66
  if @had_errors && !@debugging
68
67
  cmd = "middleman build --verbose"
69
- cmd = "bundle exec '#{cmd}'" if defined?(Bundler)
70
68
  self.shell.say "There were errors during this build, re-run with `#{cmd}` to see the full exception."
71
69
  end
72
70
 
@@ -86,7 +84,7 @@ module Middleman::Cli
86
84
  # @return [Middleman::Application]
87
85
  def shared_instance(verbose=false, instrument=false)
88
86
  @_shared_instance ||= ::Middleman::Application.server.inst do
89
- set :environment, :build
87
+ config[:environment] = :build
90
88
  logger(verbose ? 0 : 1, instrument)
91
89
  end
92
90
  end
@@ -117,7 +115,7 @@ module Middleman::Cli
117
115
  # @param [Middleman::Sitemap::Resource] resource
118
116
  # @return [String] The full path of the file that was written
119
117
  def render_to_file(resource)
120
- build_dir = self.class.shared_instance.build_dir
118
+ build_dir = self.class.shared_instance.config[:build_dir]
121
119
  output_file = File.join(build_dir, resource.destination_path)
122
120
 
123
121
  if resource.binary?
@@ -12,7 +12,7 @@ module Middleman::Cli
12
12
 
13
13
  # The setup task
14
14
  def bundle
15
- run('bundle install --without development test')#, :capture => true)
15
+ run('bundle install')#, :capture => true)
16
16
  end
17
17
  end
18
18
 
@@ -0,0 +1,46 @@
1
+ # CLI Module
2
+ module Middleman::Cli
3
+
4
+ # A thor task for creating new projects
5
+ class Console < Thor
6
+ include Thor::Actions
7
+
8
+ check_unknown_options!
9
+
10
+ namespace :console
11
+
12
+ desc "console [options]", "Start an interactive console in the context of your Middleman application"
13
+ method_option :environment,
14
+ :aliases => "-e",
15
+ :default => ENV['MM_ENV'] || ENV['RACK_ENV'] || 'development',
16
+ :desc => "The environment Middleman will run under"
17
+ method_option :verbose,
18
+ :type => :boolean,
19
+ :default => false,
20
+ :desc => 'Print debug messages'
21
+ def console
22
+ require "middleman-core"
23
+ require "irb"
24
+
25
+ opts = {
26
+ :environment => options['environment'],
27
+ :debug => options['verbose']
28
+ }
29
+
30
+ @app =::Middleman::Application.server.inst do
31
+ if opts[:environment]
32
+ set :environment, opts[:environment].to_sym
33
+ end
34
+
35
+ logger(opts[:debug] ? 0 : 1, opts[:instrumenting] || false)
36
+ end
37
+
38
+ # TODO: get file watcher / reload! working in console
39
+
40
+ IRB.setup nil
41
+ IRB.conf[:MAIN_CONTEXT] = IRB::Irb.new.context
42
+ require 'irb/ext/multi-irb'
43
+ IRB.irb nil, @app
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,235 @@
1
+ module Middleman
2
+ module Configuration
3
+ # Access to a global configuration manager for the whole Middleman project,
4
+ # plus backwards compatibility mechanisms for older Middleman projects.
5
+ module Global
6
+ def self.included(app)
7
+ app.send :extend, ClassMethods
8
+ end
9
+
10
+ module ClassMethods
11
+ # Global configuration for the whole Middleman project.
12
+ # @return [ConfigurationManager]
13
+ def config
14
+ @_config ||= ConfigurationManager.new
15
+ end
16
+
17
+ # Set attributes (global variables)
18
+ #
19
+ # @deprecated Prefer accessing settings through "config".
20
+ #
21
+ # @param [Symbol] key Name of the attribue
22
+ # @param default Attribute value
23
+ # @return [void]
24
+ def set(key, default=nil, &block)
25
+ config.define_setting(key, default)
26
+ @inst.set(key, default, &block) if @inst
27
+ end
28
+
29
+ # Access global settings as methods, to preserve compatibility with
30
+ # old Middleman.
31
+ #
32
+ # @deprecated Prefer accessing settings through "config".
33
+ def method_missing(method, *args)
34
+ if config.defines_setting? method
35
+ config[method]
36
+ else
37
+ super
38
+ end
39
+ end
40
+
41
+ # Needed so that method_missing makes sense
42
+ def respond_to?(method, include_private = false)
43
+ super || config.defines_setting?(method)
44
+ end
45
+ end
46
+
47
+ def config
48
+ self.class.config
49
+ end
50
+
51
+ # Backwards compatibilty with old Sinatra template interface
52
+ #
53
+ # @deprecated Prefer accessing settings through "config".
54
+ #
55
+ # @return [ConfigurationManager]
56
+ alias :settings :config
57
+
58
+ # Set attributes (global variables)
59
+ #
60
+ # @deprecated Prefer accessing settings through "config".
61
+ #
62
+ # @param [Symbol] key Name of the attribue
63
+ # @param value Attribute value
64
+ # @return [void]
65
+ def set(key, value=nil, &block)
66
+ value = block if block_given?
67
+ config[key] = value
68
+ end
69
+
70
+ # Access global settings as methods, to preserve compatibility with
71
+ # old Middleman.
72
+ #
73
+ # @deprecated Prefer accessing settings through "config".
74
+ def method_missing(method, *args)
75
+ if config.defines_setting? method
76
+ config[method]
77
+ else
78
+ super
79
+ end
80
+ end
81
+
82
+ # Needed so that method_missing makes sense
83
+ def respond_to?(method, include_private = false)
84
+ super || config.defines_setting?(method)
85
+ end
86
+ end
87
+
88
+ # A class that manages a collection of documented settings.
89
+ # Can be used by extensions as well as the main Middleman
90
+ # application. Extensions should probably finalize their instance
91
+ # after defining all the settings they want to expose.
92
+ class ConfigurationManager
93
+ def initialize
94
+ # A hash from setting key to ConfigSetting instance.
95
+ @settings = {}
96
+ @finalized = false
97
+ end
98
+
99
+ # Get all settings, sorted by key, as ConfigSetting objects.
100
+ # @return [Array<ConfigSetting>]
101
+ def all_settings
102
+ @settings.values.sort_by(&:key)
103
+ end
104
+
105
+ # Get a full ConfigSetting object for the setting with the give key.
106
+ # @return [ConfigSetting]
107
+ def setting(key)
108
+ @settings[key]
109
+ end
110
+
111
+ # Get the value of a setting by key. Returns nil if there is no such setting.
112
+ # @return [Object]
113
+ def [](key)
114
+ setting = @settings[key]
115
+ setting ? setting.value : nil
116
+ end
117
+
118
+ # Set the value of a setting by key. Creates the setting if it doesn't exist.
119
+ # @param [Symbol] key
120
+ # @param [Object] val
121
+ def []=(key, val)
122
+ setting = @settings[key] || define_setting(key)
123
+ setting.value = val
124
+ end
125
+
126
+ # Allow configuration settings to be read and written via methods
127
+ def method_missing(method, *args)
128
+ if defines_setting?(method) && args.size == 0
129
+ self[method]
130
+ elsif method =~ /^(\w+)=$/ && args.size == 1
131
+ self[$1.to_sym] = args[0]
132
+ else
133
+ super
134
+ end
135
+ end
136
+
137
+ # Needed so that method_missing makes sense
138
+ def respond_to?(method, include_private = false)
139
+ super || defines_setting?(method) || (method =~ /^(\w+)=$/ && defines_setting?($1))
140
+ end
141
+
142
+ # Does this configuration manager know about the setting identified by key?
143
+ # @param [Symbol] key
144
+ # @return [Boolean]
145
+ def defines_setting?(key)
146
+ @settings.has_key?(key)
147
+ end
148
+
149
+ # Define a new setting, with optional default and user-friendly description.
150
+ # Once the configuration manager is finalized, no new settings may be defined.
151
+ #
152
+ # @param [Symbol] key
153
+ # @param [Object] default
154
+ # @param [String] description
155
+ # @return [ConfigSetting]
156
+ def define_setting(key, default=nil, description=nil)
157
+ raise "Setting #{key} doesn't exist" if @finalized
158
+ raise "Setting #{key} already defined" if @settings.has_key?(key)
159
+ raise "Setting key must be a Symbol" unless key.is_a? Symbol
160
+
161
+ @settings[key] = ConfigSetting.new(key, default, description)
162
+ end
163
+
164
+ # Switch the configuration manager is finalized, it switches to read-only
165
+ # mode and no new settings may be defined.
166
+ def finalize!
167
+ @finalized = true
168
+ self
169
+ end
170
+
171
+ # Deep duplicate of the configuration manager
172
+ def dup
173
+ ConfigurationManager.new.tap {|c| c.load_settings(self.all_settings) }
174
+ end
175
+
176
+ # Load in a list of settings
177
+ def load_settings(other_settings)
178
+ other_settings.each do |setting|
179
+ new_setting = define_setting(setting.key, setting.default, setting.description)
180
+ new_setting.value = setting.value if setting.value_set?
181
+ end
182
+ end
183
+
184
+ def to_h
185
+ hash = {}
186
+ @settings.each do |key, setting|
187
+ hash[key] = setting.value
188
+ end
189
+ hash
190
+ end
191
+
192
+ def to_s
193
+ to_h.inspect
194
+ end
195
+ end
196
+
197
+ # An individual configuration setting, with an optional default and description.
198
+ # Also models whether or not a value has been set.
199
+ class ConfigSetting
200
+ # The name of this setting
201
+ attr_accessor :key
202
+
203
+ # The default value for this setting
204
+ attr_accessor :default
205
+
206
+ # A human-friendly description of the setting
207
+ attr_accessor :description
208
+
209
+ def initialize(key, default, description)
210
+ @value_set = false
211
+ self.key = key
212
+ self.default = default
213
+ self.description = description
214
+ end
215
+
216
+ # The user-supplied value for this setting, overriding the default
217
+ def value=(value)
218
+ @value = value
219
+ @value_set = true
220
+ end
221
+
222
+ # The effective value of the setting, which may be the default
223
+ # if the user has not set a value themselves. Note that even if the
224
+ # user sets the value to nil it will override the default.
225
+ def value
226
+ value_set? ? @value : default
227
+ end
228
+
229
+ # Whether or not there has been a value set beyond the default
230
+ def value_set?
231
+ @value_set
232
+ end
233
+ end
234
+ end
235
+ end
@@ -4,9 +4,6 @@ require "middleman-core/core_extensions/request"
4
4
  # File Change Notifier
5
5
  require "middleman-core/core_extensions/file_watcher"
6
6
 
7
- # Add Builder callbacks
8
- require "middleman-core/core_extensions/builder"
9
-
10
7
  # Custom Feature API
11
8
  require "middleman-core/core_extensions/extensions"
12
9
 
@@ -28,6 +25,3 @@ require "middleman-core/core_extensions/routing"
28
25
 
29
26
  # Catch and show exceptions at the Rack level
30
27
  require "middleman-core/core_extensions/show_exceptions"
31
-
32
- # Manage Ruby string encodings
33
- require "middleman-core/core_extensions/ruby_encoding"
@@ -13,7 +13,7 @@ module Middleman
13
13
  require "yaml"
14
14
  require "active_support/json"
15
15
 
16
- app.set :data_dir, "data"
16
+ app.config.define_setting :data_dir, "data", "The directory data files are stored in"
17
17
  app.send :include, InstanceMethods
18
18
  end
19
19
  alias :included :registered
@@ -25,11 +25,11 @@ module Middleman
25
25
  # parsing config.rb
26
26
  def initialize
27
27
  self.files.changed DataStore.matcher do |file|
28
- self.data.touch_file(file) if file.start_with?("#{self.data_dir}/")
28
+ self.data.touch_file(file) if file.start_with?("#{config[:data_dir]}/")
29
29
  end
30
30
 
31
31
  self.files.deleted DataStore.matcher do |file|
32
- self.data.remove_file(file) if file.start_with?("#{self.data_dir}/")
32
+ self.data.remove_file(file) if file.start_with?("#{config[:data_dir]}/")
33
33
  end
34
34
 
35
35
  super
@@ -61,7 +61,7 @@ module Middleman
61
61
  #
62
62
  # @param [Symbol] name Name of the data, used for namespacing
63
63
  # @param [Hash] content The content for this data
64
- # @return [void]
64
+ # @return [Hash]
65
65
  def store(name=nil, content=nil)
66
66
  @_local_sources ||= {}
67
67
  @_local_sources[name.to_s] = content unless name.nil? || content.nil?
@@ -72,7 +72,7 @@ module Middleman
72
72
  #
73
73
  # @param [Symbol] name Name of the data, used for namespacing
74
74
  # @param [Proc] proc The callback which will return data
75
- # @return [void]
75
+ # @return [Hash]
76
76
  def callbacks(name=nil, proc=nil)
77
77
  @_callback_sources ||= {}
78
78
  @_callback_sources[name.to_s] = proc unless name.nil? || proc.nil?
@@ -92,19 +92,30 @@ module Middleman
92
92
  # @param [String] file The file to be re-parsed
93
93
  # @return [void]
94
94
  def touch_file(file)
95
- file = File.expand_path(file, @app.root)
95
+ root = Pathname(@app.root)
96
+ full_path = root + file
96
97
  extension = File.extname(file)
97
98
  basename = File.basename(file, extension)
98
99
 
100
+ data_path = full_path.relative_path_from(root + @app.config[:data_dir])
101
+
99
102
  if %w(.yaml .yml).include?(extension)
100
- data = YAML.load_file(file)
103
+ data = YAML.load_file(full_path)
101
104
  elsif extension == ".json"
102
- data = ActiveSupport::JSON.decode(File.read(file))
105
+ data = ActiveSupport::JSON.decode(full_path.read)
103
106
  else
104
107
  return
105
108
  end
106
109
 
107
- @local_data[basename] = ::Middleman::Util.recursively_enhance(data)
110
+ data_branch = @local_data
111
+
112
+ path = data_path.to_s.split(File::SEPARATOR)[0..-2]
113
+ path.each do |dir|
114
+ data_branch[dir] ||= ::Middleman::Util.recursively_enhance({})
115
+ data_branch = data_branch[dir]
116
+ end
117
+
118
+ data_branch[basename] = ::Middleman::Util.recursively_enhance(data)
108
119
  end
109
120
 
110
121
  # Remove a given file from the internal cache
@@ -112,12 +123,24 @@ module Middleman
112
123
  # @param [String] file The file to be cleared
113
124
  # @return [void]
114
125
  def remove_file(file)
126
+ root = Pathname(@app.root)
127
+ full_path = root + file
115
128
  extension = File.extname(file)
116
129
  basename = File.basename(file, extension)
117
- @local_data.delete(basename) if @local_data.has_key?(basename)
130
+
131
+ data_path = full_path.relative_path_from(root + @app.config[:data_dir])
132
+
133
+ data_branch = @local_data
134
+
135
+ path = data_path.to_s.split(File::SEPARATOR)[0..-2]
136
+ path.each do |dir|
137
+ data_branch = data_branch[dir]
138
+ end
139
+
140
+ data_branch.delete(basename) if data_branch.has_key?(basename)
118
141
  end
119
142
 
120
- # Get a hash hash from either internal static data or a callback
143
+ # Get a hash from either internal static data or a callback
121
144
  #
122
145
  # @param [String, Symbol] path The name of the data namespace
123
146
  # @return [Hash, nil]