Deep freeze IndifferentAccess.

This commit is contained in:
Thomas Reynolds 2014-07-14 13:19:34 -07:00
parent 332ce2bebc
commit 1f3e2043cb
9 changed files with 74 additions and 49 deletions

View file

@ -17,6 +17,16 @@ if ENV['TEST'] || ENV['CONTRACTS'] == 'true'
end
end
class Frozen < CallableClass
def initialize(contract)
@contract = contract
end
def valid?(val)
val.frozen? && Contract.valid?(val, @contract)
end
end
class ArrayOf
def initialize(contract)
@contract = contract.is_a?(String) ? IsA[contract] : contract
@ -128,6 +138,9 @@ else
class SetOf < Callable
end
class Frozen < Callable
end
end
end

View file

@ -97,11 +97,11 @@ module Middleman
path = data_path.to_s.split(File::SEPARATOR)[0..-2]
path.each do |dir|
data_branch[dir] ||= ::Middleman::Util.recursively_enhance({})
data_branch[dir] ||= {}
data_branch = data_branch[dir]
end
data_branch[basename] = data && ::Middleman::Util.recursively_enhance(data)
data_branch[basename] = data
end
# Remove a given file from the internal cache
@ -132,14 +132,13 @@ module Middleman
# @return [Hash, nil]
Contract Or[String, Symbol] => Maybe[Hash]
def data_for_path(path)
response = nil
if store.key?(path.to_s)
response = store[path.to_s]
response = if store.key?(path.to_s)
store[path.to_s]
elsif callbacks.key?(path.to_s)
response = callbacks[path.to_s].call
callbacks[path.to_s].call
end
response = ::Middleman::Util.recursively_enhance(response)
response
end
@ -149,10 +148,11 @@ module Middleman
# @return [Hash, nil]
def method_missing(path)
if @local_data.key?(path.to_s)
@local_data[path.to_s] = ::Middleman::Util.recursively_enhance(@local_data[path.to_s])
return @local_data[path.to_s]
else
result = data_for_path(path)
return ::Middleman::Util.recursively_enhance(result) if result
return result if result
end
super

View file

@ -51,7 +51,7 @@ module Middleman
# Merge per-extension options from config
extension = File.extname(path)
options = opts.dup.merge(options_for_ext(extension))
options = opts.merge(options_for_ext(extension))
options[:outvar] ||= '@_out_buf'
options.delete(:layout)

View file

@ -17,7 +17,7 @@ module Middleman
# Start an instance of Middleman::Application
# @return [void]
def start(opts={})
@options = opts
@options = opts.dup.freeze
@host = @options[:host] || '0.0.0.0'
@port = @options[:port] || DEFAULT_PORT
@ -94,7 +94,7 @@ module Middleman
private
def new_app
opts = @options.dup
opts = @options
::Middleman::Logger.singleton(
opts[:debug] ? 0 : 1,

View file

@ -79,7 +79,7 @@ module Middleman
Contract None => IsA['Middleman::Util::HashWithIndifferentAccess']
def data
# TODO: Should this really be a HashWithIndifferentAccess?
::Middleman::Util.recursively_enhance(metadata[:page]).freeze
::Middleman::Util.recursively_enhance(metadata[:page])
end
# Options about how this resource is rendered, such as its :layout,

View file

@ -30,10 +30,10 @@ module Middleman
# @param [Middleman::Application] app
# @param [Hash] locs
# @param [Hash] opts
def initialize(app, locs={}, opts={})
def initialize(app, locs={}.freeze, opts={}.freeze)
@app = app
@locs = locs.dup.freeze
@opts = opts.dup.freeze
@locs = locs
@opts = opts
end
# Return the current buffer to the caller and clear the value internally.

View file

@ -31,16 +31,19 @@ module Middleman
Contract Hash, Hash => String
def render(locs={}, opts={})
path = @path.dup
locals = locs.dup.freeze
options = opts.dup
extension = File.extname(path)
engine = extension[1..-1].to_sym
if defined?(::I18n)
old_locale = ::I18n.locale
::I18n.locale = opts[:lang] if opts[:lang]
::I18n.locale = options[:lang] if options[:lang]
end
# Sandboxed class for template eval
context = @app.template_context_class.new(@app, locs, opts)
context = @app.template_context_class.new(@app, locals, options)
# TODO: Only for HAML files
context.init_haml_helpers if context.respond_to?(:init_haml_helpers)
@ -50,10 +53,10 @@ module Middleman
content = nil
while ::Tilt[path]
begin
opts[:template_body] = content if content
options[:template_body] = content if content
content_renderer = ::Middleman::FileRenderer.new(@app, path)
content = content_renderer.render(locs, opts, context)
content = content_renderer.render(locals, options, context)
path = File.basename(path, File.extname(path))
rescue LocalJumpError
@ -62,9 +65,9 @@ module Middleman
end
# If we need a layout and have a layout, use it
if layout_path = fetch_layout(engine, opts)
if layout_path = fetch_layout(engine, options)
layout_renderer = ::Middleman::FileRenderer.new(@app, layout_path)
content = layout_renderer.render(locs, opts, context) { content }
content = layout_renderer.render(locals, options, context) { content }
end
# Return result

View file

@ -15,11 +15,11 @@ require 'rack/mime'
require 'middleman-core/contracts'
module Middleman
module Util
extend self
include Contracts
module_function
# Whether the source file is binary.
#
# @param [String] filename The file to check.
@ -74,32 +74,16 @@ module Middleman
# @private
# @param [Hash] data Normal hash
# @return [Middleman::Util::HashWithIndifferentAccess]
Contract Or[Hash, Array] => Or[HashWithIndifferentAccess, Array]
Contract Maybe[Or[Array, Hash, HashWithIndifferentAccess]] => Maybe[Frozen[Or[HashWithIndifferentAccess, Array]]]
def recursively_enhance(data)
if data.is_a? Hash
enhanced = ::Middleman::Util::HashWithIndifferentAccess.new(data)
enhanced.each do |key, val|
enhanced[key] = if val.is_a?(Hash) || val.is_a?(Array)
recursively_enhance(val)
else
val
end
end
enhanced
if data.is_a? HashWithIndifferentAccess
data
elsif data.is_a? Hash
HashWithIndifferentAccess.new(data)
elsif data.is_a? Array
enhanced = data.dup
enhanced.each_with_index do |val, i|
enhanced[i] = if val.is_a?(Hash) || val.is_a?(Array)
recursively_enhance(val)
else
val
end
end
enhanced
data.map(&method(:recursively_enhance))
else
nil
end
end

View file

@ -1,3 +1,5 @@
require 'middleman-core/contracts'
module Middleman
module Util
# A hash with indifferent access and magic predicates.
@ -10,11 +12,17 @@ module Middleman
# hash.foo? #=> true
#
class HashWithIndifferentAccess < ::Hash #:nodoc:
include Contracts
Contract Hash => Any
def initialize(hash={})
super()
hash.each do |key, value|
self[convert_key(key)] = value
hash.each do |key, val|
self[key] = recursively_enhance(val)
end
freeze
end
def [](key)
@ -73,6 +81,23 @@ module Middleman
self[method]
end
end
private
Contract Any => Frozen[Any]
def recursively_enhance(data)
if data.is_a? HashWithIndifferentAccess
data
elsif data.is_a? Hash
self.class.new(data)
elsif data.is_a? Array
data.map(&method(:recursively_enhance)).freeze
elsif data.frozen?
data
else
data.dup.freeze
end
end
end
end
end