Replace Hamster hash for user-accessible data with Hashie Indifferent access

This commit is contained in:
Thomas Reynolds 2015-08-12 15:24:35 -07:00
parent fb6bca234f
commit bb6b8c7f13
7 changed files with 23 additions and 83 deletions

View file

@ -18,16 +18,6 @@ if ENV['TEST'] || ENV['CONTRACTS'] == 'true'
end end
end end
class Frozen < CallableClass
def initialize(contract)
@contract = contract
end
def valid?(val)
(val.frozen? || val.nil? || [::TrueClass, ::FalseClass, ::Fixnum].include?(val.class)) && Contract.valid?(val, @contract)
end
end
VectorOf = Contracts::CollectionOf::Factory.new(::Hamster::Vector) VectorOf = Contracts::CollectionOf::Factory.new(::Hamster::Vector)
ResourceList = Contracts::ArrayOf[IsA['Middleman::Sitemap::Resource']] ResourceList = Contracts::ArrayOf[IsA['Middleman::Sitemap::Resource']]
end end

View file

@ -87,8 +87,8 @@ module Middleman
end end
# Data about this resource, populated from frontmatter or extensions. # Data about this resource, populated from frontmatter or extensions.
# @return [IndifferentHash] # @return [Hash]
Contract IsA['Middleman::Util::IndifferentHash'] Contract RespondTo[:indifferent_access?]
def data def data
::Middleman::Util.recursively_enhance(metadata[:page]) ::Middleman::Util.recursively_enhance(metadata[:page])
end end

View file

@ -11,8 +11,8 @@ require 'rack/mime'
# DbC # DbC
require 'middleman-core/contracts' require 'middleman-core/contracts'
# Immutable Data # Indifferent Access
require 'hamster' require 'hashie'
# For URI templating # For URI templating
require 'addressable/uri' require 'addressable/uri'
@ -76,56 +76,25 @@ module Middleman
end end
end end
class IndifferentHash < ::Hamster::Hash class EnhancedHash < ::Hash
def [](key) include ::Hashie::Extensions::MergeInitializer
if key?(key.to_sym) include ::Hashie::Extensions::MethodReader
super(key.to_sym) include ::Hashie::Extensions::IndifferentAccess
elsif key?(key.to_s)
super(key.to_s)
else
super
end
end end
def method_missing(key, *_args) # Recursively convert a normal Hash into a EnhancedHash
if key?(key.to_sym)
self[key.to_sym]
elsif key?(key.to_s)
self[key.to_s]
end
end
end
# Recursively convert a normal Hash into a IndifferentHash
# #
# @private # @private
# @param [Hash] data Normal hash # @param [Hash] data Normal hash
# @return [Middleman::Util::IndifferentHash] # @return [Hash]
FrozenDataStructure = Frozen[Or[IndifferentHash, Array, String, TrueClass, FalseClass, Fixnum, NilClass]] Contract Maybe[Hash] => Maybe[Or[Array, EnhancedHash]]
Contract Maybe[Or[String, Array, Hash, IndifferentHash]] => Maybe[FrozenDataStructure]
def recursively_enhance(obj) def recursively_enhance(obj)
case obj if obj.is_a? ::Array
when ::Hash obj.map { |e| recursively_enhance(e) }.freeze
res = obj.map { |key, value| [recursively_enhance(key), recursively_enhance(value)] } elsif obj.is_a? ::Hash
IndifferentHash.new(res) EnhancedHash.new(obj).freeze
when IndifferentHash
obj.map { |key, value| [recursively_enhance(key), recursively_enhance(value)] }
when ::Array
res = obj.map { |element| recursively_enhance(element) }
Hamster::Vector.new(res)
when ::SortedSet
# This clause must go before ::Set clause, since ::SortedSet is a ::Set.
res = obj.map { |element| recursively_enhance(element) }
Hamster::SortedSet.new(res)
when ::Set
res = obj.map { |element| recursively_enhance(element) }
Hamster::Set.new(res)
when Hamster::Vector, Hamster::Set, Hamster::SortedSet
obj.map { |element| recursively_enhance(element) }
when ::TrueClass, ::FalseClass, ::Fixnum, ::Symbol, ::NilClass, ::Float
obj
else else
obj.dup.freeze obj
end end
end end

View file

@ -29,7 +29,7 @@ module Middleman
# Get the frontmatter and plain content from a file # Get the frontmatter and plain content from a file
# @param [String] path # @param [String] path
# @return [Array<Middleman::Util::IndifferentHash, String>] # @return [Array<Hash, String>]
Contract Pathname, Maybe[Symbol] => [Hash, Maybe[String]] Contract Pathname, Maybe[Symbol] => [Hash, Maybe[String]]
def parse(full_path, known_type=nil) def parse(full_path, known_type=nil)
data = {} data = {}

View file

@ -1,5 +1,5 @@
module Middleman module Middleman
# Current Version # Current Version
# @return [String] # @return [String]
VERSION = '4.0.0.beta.2' unless const_defined?(:VERSION) VERSION = '4.0.0.beta.3' unless const_defined?(:VERSION)
end end

View file

@ -52,6 +52,7 @@ Gem::Specification.new do |s|
# Testing # Testing
s.add_dependency('contracts', ['~> 0.9.0']) s.add_dependency('contracts', ['~> 0.9.0'])
# Immutability # Hash stuff
s.add_dependency('hashie', ['~> 3.4'])
s.add_dependency('hamster', ['~> 1.0']) s.add_dependency('hamster', ['~> 1.0'])
end end

View file

@ -54,43 +54,23 @@ describe Middleman::Util do
end end
describe "::recursively_enhance" do describe "::recursively_enhance" do
it "returns IndifferentHash if given one" do it "returns Hashie extended Hash if given a hash" do
input = Middleman::Util::IndifferentHash.new({test: "subject"})
subject = Middleman::Util.recursively_enhance input
expect( subject ).to be_a Middleman::Util::IndifferentHash
expect( subject.test ).to eq "subject"
end
it "returns IndifferentHash if given a hash" do
input = {test: "subject"} input = {test: "subject"}
subject = Middleman::Util.recursively_enhance input subject = Middleman::Util.recursively_enhance input
expect( subject ).to be_a Middleman::Util::IndifferentHash
expect( subject.test ).to eq "subject" expect( subject.test ).to eq "subject"
end end
it "returns Array with strings, or IndifferentHash, true, false" do it "returns Array with strings, or IndifferentHash, true, false" do
indifferent_hash = Middleman::Util::IndifferentHash.new({test: "subject"}) indifferent_hash = {test: "subject"}
regular_hash = {regular: "hash"} regular_hash = {regular: "hash"}
input = [ indifferent_hash, regular_hash, true, false ] input = [ indifferent_hash, regular_hash, true, false ]
subject = Middleman::Util.recursively_enhance input subject = Middleman::Util.recursively_enhance input
expect( subject[0] ).to be_a Middleman::Util::IndifferentHash
expect( subject[1] ).to be_a Middleman::Util::IndifferentHash
expect( subject[1].regular ).to eq "hash" expect( subject[1].regular ).to eq "hash"
expect( subject[2] ).to eq true expect( subject[2] ).to eq true
expect( subject[3] ).to eq false expect( subject[3] ).to eq false
end end
it "returns duplicated & frozen original object if not special cased" do
input = "foo"
subject = Middleman::Util.recursively_enhance input
expect( subject ).to eq input
expect( subject.object_id ).not_to eq input.object_id
expect( subject ).to be_frozen
end
end end
end end