pancake 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/pancake +96 -0
- data/lib/pancake.rb +5 -7
- data/lib/pancake/configuration.rb +2 -23
- data/lib/pancake/core_ext/object.rb +1 -1
- data/lib/pancake/defaults/configuration.rb +21 -1
- data/lib/pancake/generators.rb +0 -3
- data/lib/pancake/hooks/inheritable_inner_classes.rb +15 -2
- data/lib/pancake/master.rb +34 -1
- data/lib/pancake/mixins/render.rb +1 -2
- data/lib/pancake/mixins/stack_helper.rb +1 -1
- data/lib/pancake/router.rb +18 -63
- data/lib/pancake/stack/configuration.rb +3 -5
- data/lib/pancake/stack/router.rb +10 -33
- data/lib/pancake/stack/stack.rb +70 -50
- data/{spec/helpers → lib/pancake/test}/helpers.rb +3 -3
- data/lib/pancake/test/matchers.rb +20 -0
- data/lib/pancake/vendor/hashie/.document +5 -0
- data/lib/pancake/vendor/hashie/.gitignore +7 -0
- data/lib/pancake/vendor/hashie/Gemfile +11 -0
- data/lib/pancake/vendor/hashie/Gemfile.lock +25 -0
- data/lib/pancake/{generators/templates/short/%stack_name%/LICENSE.tt → vendor/hashie/LICENSE} +1 -1
- data/lib/pancake/vendor/hashie/README.rdoc +101 -0
- data/lib/pancake/{generators/templates/micro/%stack_name%/Rakefile.tt → vendor/hashie/Rakefile} +3 -13
- data/lib/pancake/vendor/hashie/VERSION +1 -0
- data/lib/pancake/vendor/hashie/hashie.gemspec +33 -0
- data/lib/pancake/vendor/hashie/lib/hashie.rb +5 -0
- data/lib/pancake/vendor/hashie/lib/hashie/clash.rb +86 -0
- data/lib/pancake/vendor/hashie/lib/hashie/dash.rb +108 -0
- data/lib/pancake/vendor/hashie/lib/hashie/hash.rb +22 -0
- data/lib/pancake/vendor/hashie/lib/hashie/hash_extensions.rb +49 -0
- data/lib/pancake/vendor/hashie/lib/hashie/mash.rb +148 -0
- data/lib/pancake/vendor/hashie/spec/hashie/clash_spec.rb +42 -0
- data/lib/pancake/vendor/hashie/spec/hashie/dash_spec.rb +103 -0
- data/lib/pancake/vendor/hashie/spec/hashie/hash_spec.rb +22 -0
- data/lib/pancake/vendor/hashie/spec/hashie/mash_spec.rb +135 -0
- data/lib/pancake/vendor/hashie/spec/spec.opts +2 -0
- data/lib/pancake/{generators/templates/short/%stack_name%/spec/spec_helper.rb.tt → vendor/hashie/spec/spec_helper.rb} +7 -7
- data/spec/pancake/configuration_spec.rb +1 -1
- data/spec/pancake/constants_spec.rb +1 -1
- data/spec/pancake/defaults/configuration_spec.rb +1 -1
- data/spec/pancake/hooks/on_inherit_spec.rb +13 -13
- data/spec/pancake/inheritance_spec.rb +22 -22
- data/spec/pancake/middleware_spec.rb +6 -5
- data/spec/pancake/middlewares/logger_spec.rb +1 -1
- data/spec/pancake/middlewares/static_spec.rb +1 -1
- data/spec/pancake/mime_types_spec.rb +1 -1
- data/spec/pancake/mixins/publish_spec.rb +24 -24
- data/spec/pancake/mixins/render/template_spec.rb +1 -1
- data/spec/pancake/mixins/render/view_context_spec.rb +1 -1
- data/spec/pancake/mixins/render_spec.rb +1 -1
- data/spec/pancake/mixins/request_helper_spec.rb +1 -1
- data/spec/pancake/mixins/stack_helper_spec.rb +3 -3
- data/spec/pancake/pancake_spec.rb +1 -1
- data/spec/pancake/paths_spec.rb +30 -30
- data/spec/pancake/stack/router_spec.rb +24 -62
- data/spec/pancake/stack/stack_configuration_spec.rb +1 -1
- data/spec/pancake/stack/stack_spec.rb +47 -4
- data/spec/spec_helper.rb +3 -3
- metadata +56 -128
- data/bin/pancake-gen +0 -30
- data/lib/pancake/bootloaders.rb +0 -187
- data/lib/pancake/generators/base.rb +0 -12
- data/lib/pancake/generators/micro_generator.rb +0 -17
- data/lib/pancake/generators/short_generator.rb +0 -18
- data/lib/pancake/generators/templates/common/dotgitignore +0 -22
- data/lib/pancake/generators/templates/common/dothtaccess +0 -17
- data/lib/pancake/generators/templates/micro/%stack_name%/%stack_name%.rb.tt +0 -8
- data/lib/pancake/generators/templates/micro/%stack_name%/config.ru.tt +0 -12
- data/lib/pancake/generators/templates/micro/%stack_name%/pancake_init.rb.tt +0 -1
- data/lib/pancake/generators/templates/micro/%stack_name%/public/.empty_directory +0 -0
- data/lib/pancake/generators/templates/micro/%stack_name%/tmp/.empty_directory +0 -0
- data/lib/pancake/generators/templates/micro/%stack_name%/views/root.html.haml +0 -1
- data/lib/pancake/generators/templates/short/%stack_name%/README.tt +0 -7
- data/lib/pancake/generators/templates/short/%stack_name%/Rakefile.tt +0 -56
- data/lib/pancake/generators/templates/short/%stack_name%/VERSION.tt +0 -1
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%.rb.tt +0 -12
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/%stack_name%.rb.tt +0 -6
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config.ru.tt +0 -10
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config/config.rb.tt +0 -23
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config/environments/development.rb.tt +0 -15
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config/environments/production.rb.tt +0 -16
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config/environments/staging.rb.tt +0 -17
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/models/.empty_directory +0 -0
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/mounts/.empty_directory +0 -0
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/public/.empty_directory +0 -0
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/tasks/%stack_name%.rake.tt +0 -4
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/tmp/.empty_directory +0 -0
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/views/root.html.haml +0 -2
- data/lib/pancake/generators/templates/short/%stack_name%/pancake_init.rb.tt +0 -1
- data/lib/pancake/generators/templates/short/%stack_name%/spec/%stack_name%_spec.rb.tt +0 -11
- data/lib/pancake/stack/app.rb +0 -10
- data/lib/pancake/stack/bootloader.rb +0 -114
- data/lib/pancake/stack/middleware.rb +0 -0
- data/lib/pancake/stacks/short.rb +0 -3
- data/lib/pancake/stacks/short/bootloaders.rb +0 -5
- data/lib/pancake/stacks/short/controller.rb +0 -184
- data/lib/pancake/stacks/short/default/views/base.html.haml +0 -5
- data/lib/pancake/stacks/short/default/views/error.html.haml +0 -12
- data/lib/pancake/stacks/short/stack.rb +0 -207
- data/spec/helpers/matchers.rb +0 -25
- data/spec/pancake/bootloaders_spec.rb +0 -119
- data/spec/pancake/stack/app_spec.rb +0 -28
- data/spec/pancake/stack/bootloader_spec.rb +0 -41
- data/spec/pancake/stack/middleware_spec.rb +0 -0
- data/spec/pancake/stacks/short/controller_spec.rb +0 -442
- data/spec/pancake/stacks/short/middlewares_spec.rb +0 -22
- data/spec/pancake/stacks/short/router_spec.rb +0 -150
- data/spec/pancake/stacks/short/stack_spec.rb +0 -117
data/lib/pancake/{generators/templates/micro/%stack_name%/Rakefile.tt → vendor/hashie/Rakefile}
RENAMED
@@ -1,11 +1,5 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rake'
|
3
|
-
require 'pancake'
|
4
|
-
|
5
|
-
require File.join(Pancake.get_root(__FILE__), "<%= stack_name %>")
|
6
|
-
Pancake.root = Pancake.get_root(__FILE__)
|
7
|
-
THIS_STACK = <%= stack_name.camel_case %>
|
8
|
-
<%= stack_name.camel_case %>.load_rake_tasks!(:master => true)
|
9
3
|
|
10
4
|
require 'spec/rake/spectask'
|
11
5
|
Spec::Rake::SpecTask.new(:spec) do |spec|
|
@@ -19,22 +13,18 @@ Spec::Rake::SpecTask.new(:rcov) do |spec|
|
|
19
13
|
spec.rcov = true
|
20
14
|
end
|
21
15
|
|
22
|
-
|
23
16
|
task :default => :spec
|
24
17
|
|
25
18
|
require 'rake/rdoctask'
|
26
19
|
Rake::RDocTask.new do |rdoc|
|
27
|
-
if File.exist?('VERSION
|
28
|
-
|
29
|
-
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
20
|
+
if File.exist?('VERSION')
|
21
|
+
version = File.read('VERSION')
|
30
22
|
else
|
31
23
|
version = ""
|
32
24
|
end
|
33
25
|
|
34
26
|
rdoc.rdoc_dir = 'rdoc'
|
35
|
-
rdoc.title = "
|
27
|
+
rdoc.title = "hashie #{version}"
|
36
28
|
rdoc.rdoc_files.include('README*')
|
37
29
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
38
30
|
end
|
39
|
-
|
40
|
-
|
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'bundler'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = %q{hashie}
|
7
|
+
s.version = File.read("VERSION")
|
8
|
+
|
9
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
10
|
+
s.authors = ["Michael Bleigh"]
|
11
|
+
s.date = %q{2010-03-05}
|
12
|
+
s.description = %q{Hashie is a small collection of tools that make hashes more powerful. Currently includes Mash (Mocking Hash) and Dash (Discrete Hash).}
|
13
|
+
s.email = %q{michael@intridea.com}
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"LICENSE",
|
16
|
+
"README.rdoc"
|
17
|
+
]
|
18
|
+
s.files = Dir["**/*"]
|
19
|
+
s.add_bundler_dependencies
|
20
|
+
s.homepage = %q{http://github.com/intridea/hashie}
|
21
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
s.rubygems_version = %q{1.3.6}
|
24
|
+
s.summary = %q{Your friendly neighborhood hash toolkit.}
|
25
|
+
s.test_files = [
|
26
|
+
"spec/hashie/clash_spec.rb",
|
27
|
+
"spec/hashie/dash_spec.rb",
|
28
|
+
"spec/hashie/hash_spec.rb",
|
29
|
+
"spec/hashie/mash_spec.rb",
|
30
|
+
"spec/spec_helper.rb"
|
31
|
+
]
|
32
|
+
end
|
33
|
+
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'hashie/hash'
|
2
|
+
|
3
|
+
module Hashie
|
4
|
+
#
|
5
|
+
# A Clash is a "Chainable Lazy Hash". Inspired by libraries such as Arel,
|
6
|
+
# a Clash allows you to chain together method arguments to build a
|
7
|
+
# hash, something that's especially useful if you're doing something
|
8
|
+
# like constructing a complex options hash. Here's a basic example:
|
9
|
+
#
|
10
|
+
# c = Hashie::Clash.new.conditions(:foo => 'bar').order(:created_at)
|
11
|
+
# c # => {:conditions => {:foo => 'bar'}, :order => :created_at}
|
12
|
+
#
|
13
|
+
# Clash provides another way to create sub-hashes by using bang notation.
|
14
|
+
# You can dive into a sub-hash by providing a key with a bang and dive
|
15
|
+
# back out again with the _end! method. Example:
|
16
|
+
#
|
17
|
+
# c = Hashie::Clash.new.conditions!.foo('bar').baz(123)._end!.order(:created_at)
|
18
|
+
# c # => {:conditions => {:foo => 'bar', :baz => 123}, :order => :created_at}
|
19
|
+
#
|
20
|
+
# Because the primary functionality of Clash is to build options objects,
|
21
|
+
# all keys are converted to symbols since many libraries expect symbols explicitly
|
22
|
+
# for keys.
|
23
|
+
#
|
24
|
+
class Clash < ::Hash
|
25
|
+
class ChainError < ::StandardError; end
|
26
|
+
# The parent Clash if this Clash was created via chaining.
|
27
|
+
attr_reader :_parent
|
28
|
+
|
29
|
+
# Initialize a new clash by passing in a Hash to
|
30
|
+
# convert and, optionally, the parent to which this
|
31
|
+
# Clash is chained.
|
32
|
+
def initialize(other_hash = {}, parent = nil)
|
33
|
+
@_parent = parent
|
34
|
+
other_hash.each_pair do |k, v|
|
35
|
+
self[k.to_sym] = v
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Jump back up a level if you are using bang method
|
40
|
+
# chaining. For example:
|
41
|
+
#
|
42
|
+
# c = Hashie::Clash.new.foo('bar')
|
43
|
+
# c.baz!.foo(123) # => c[:baz]
|
44
|
+
# c.baz!._end! # => c
|
45
|
+
def _end!
|
46
|
+
self._parent
|
47
|
+
end
|
48
|
+
|
49
|
+
def id(*args) #:nodoc:
|
50
|
+
method_missing(:id, *args)
|
51
|
+
end
|
52
|
+
|
53
|
+
def merge_store(key, *args) #:nodoc:
|
54
|
+
case args.length
|
55
|
+
when 1
|
56
|
+
val = args.first
|
57
|
+
val = self[key].merge(val) if self[key].is_a?(::Hash) && val.is_a?(::Hash)
|
58
|
+
else
|
59
|
+
val = args
|
60
|
+
end
|
61
|
+
|
62
|
+
self[key.to_sym] = val
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
def method_missing(name, *args) #:nodoc:
|
67
|
+
name = name.to_s
|
68
|
+
if name.match(/!$/) && args.empty?
|
69
|
+
key = name[0...-1].to_sym
|
70
|
+
|
71
|
+
if self[key].nil?
|
72
|
+
self[key] = Clash.new({}, self)
|
73
|
+
elsif self[key].is_a?(::Hash) && !self[key].is_a?(Clash)
|
74
|
+
self[key] = Clash.new(self[key], self)
|
75
|
+
else
|
76
|
+
raise ChainError, "Tried to chain into a non-hash key."
|
77
|
+
end
|
78
|
+
|
79
|
+
self[key]
|
80
|
+
elsif args.any?
|
81
|
+
key = name.to_sym
|
82
|
+
self.merge_store(key, *args)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'hashie/hash'
|
2
|
+
|
3
|
+
module Hashie
|
4
|
+
include Hashie::PrettyInspect
|
5
|
+
# A Dash is a 'defined' or 'discrete' Hash, that is, a Hash
|
6
|
+
# that has a set of defined keys that are accessible (with
|
7
|
+
# optional defaults) and only those keys may be set or read.
|
8
|
+
#
|
9
|
+
# Dashes are useful when you need to create a very simple
|
10
|
+
# lightweight data object that needs even fewer options and
|
11
|
+
# resources than something like a DataMapper resource.
|
12
|
+
#
|
13
|
+
# It is preferrable to a Struct because of the in-class
|
14
|
+
# API for defining properties as well as per-property defaults.
|
15
|
+
class Dash < Hashie::Hash
|
16
|
+
include Hashie::PrettyInspect
|
17
|
+
alias_method :to_s, :inspect
|
18
|
+
|
19
|
+
# Defines a property on the Dash. Options are
|
20
|
+
# as follows:
|
21
|
+
#
|
22
|
+
# * <tt>:default</tt> - Specify a default value for this property,
|
23
|
+
# to be returned before a value is set on the property in a new
|
24
|
+
# Dash.
|
25
|
+
#
|
26
|
+
def self.property(property_name, options = {})
|
27
|
+
property_name = property_name.to_sym
|
28
|
+
|
29
|
+
(@properties ||= []) << property_name
|
30
|
+
(@defaults ||= {})[property_name] = options.delete(:default)
|
31
|
+
|
32
|
+
class_eval <<-RUBY
|
33
|
+
def #{property_name}
|
34
|
+
self[:#{property_name}]
|
35
|
+
end
|
36
|
+
|
37
|
+
def #{property_name}=(val)
|
38
|
+
self[:#{property_name}] = val
|
39
|
+
end
|
40
|
+
RUBY
|
41
|
+
end
|
42
|
+
|
43
|
+
# Get a String array of the currently defined
|
44
|
+
# properties on this Dash.
|
45
|
+
def self.properties
|
46
|
+
properties = []
|
47
|
+
ancestors.each do |elder|
|
48
|
+
if elder.instance_variable_defined?("@properties")
|
49
|
+
properties << elder.instance_variable_get("@properties")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
properties.flatten.map{|p| p.to_s}
|
54
|
+
end
|
55
|
+
|
56
|
+
# Check to see if the specified property has already been
|
57
|
+
# defined.
|
58
|
+
def self.property?(prop)
|
59
|
+
properties.include?(prop.to_s)
|
60
|
+
end
|
61
|
+
|
62
|
+
# The default values that have been set for this Dash
|
63
|
+
def self.defaults
|
64
|
+
properties = {}
|
65
|
+
ancestors.each do |elder|
|
66
|
+
if elder.instance_variable_defined?("@defaults")
|
67
|
+
properties.merge! elder.instance_variable_get("@defaults")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
properties
|
72
|
+
end
|
73
|
+
|
74
|
+
# You may initialize a Dash with an attributes hash
|
75
|
+
# just like you would many other kinds of data objects.
|
76
|
+
def initialize(attributes = {})
|
77
|
+
self.class.properties.each do |prop|
|
78
|
+
self.send("#{prop}=", self.class.defaults[prop.to_sym])
|
79
|
+
end
|
80
|
+
|
81
|
+
attributes.each_pair do |att, value|
|
82
|
+
self.send("#{att}=", value)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Retrieve a value from the Dash (will return the
|
87
|
+
# property's default value if it hasn't been set).
|
88
|
+
def [](property)
|
89
|
+
super(property.to_sym) if property_exists? property
|
90
|
+
end
|
91
|
+
|
92
|
+
# Set a value on the Dash in a Hash-like way. Only works
|
93
|
+
# on pre-existing properties.
|
94
|
+
def []=(property, value)
|
95
|
+
super if property_exists? property
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
# Raises an NoMethodError if the property doesn't exist
|
100
|
+
#
|
101
|
+
def property_exists?(property)
|
102
|
+
unless self.class.property?(property.to_sym)
|
103
|
+
raise NoMethodError, "The property '#{property}' is not defined for this Dash."
|
104
|
+
end
|
105
|
+
true
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Hashie
|
2
|
+
# A Hashie Hash is simply a Hash that has convenience
|
3
|
+
# functions baked in such as stringify_keys that may
|
4
|
+
# not be available in all libraries.
|
5
|
+
class Hash < Hash
|
6
|
+
include Hashie::HashExtensions
|
7
|
+
|
8
|
+
# Converts a mash back to a hash (with stringified keys)
|
9
|
+
def to_hash
|
10
|
+
out = {}
|
11
|
+
keys.each do |k|
|
12
|
+
out[k] = Hashie::Hash === self[k] ? self[k].to_hash : self[k]
|
13
|
+
end
|
14
|
+
out
|
15
|
+
end
|
16
|
+
|
17
|
+
# The C geneartor for the json gem doesn't like mashies
|
18
|
+
def to_json(*args)
|
19
|
+
to_hash.to_json(*args)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Hashie
|
2
|
+
module HashExtensions
|
3
|
+
def self.included(base)
|
4
|
+
# Don't tread on existing extensions of Hash by
|
5
|
+
# adding methods that are likely to exist.
|
6
|
+
%w(stringify_keys stringify_keys!).each do |hashie_method|
|
7
|
+
base.send :alias_method, hashie_method, "hashie_#{hashie_method}" unless base.instance_methods.include?(hashie_method)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# Destructively convert all of the keys of a Hash
|
12
|
+
# to their string representations.
|
13
|
+
def hashie_stringify_keys!
|
14
|
+
self.keys.each do |k|
|
15
|
+
unless String === k
|
16
|
+
self[k.to_s] = self.delete(k)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
# Convert all of the keys of a Hash
|
23
|
+
# to their string representations.
|
24
|
+
def hashie_stringify_keys
|
25
|
+
self.dup.stringify_keys!
|
26
|
+
end
|
27
|
+
|
28
|
+
# Convert this hash into a Mash
|
29
|
+
def to_mash
|
30
|
+
Hashie::Mash.new(self)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module PrettyInspect
|
35
|
+
def self.included(base)
|
36
|
+
base.send :alias_method, :hash_inspect, :inspect
|
37
|
+
base.send :alias_method, :inspect, :hashie_inspect
|
38
|
+
end
|
39
|
+
|
40
|
+
def hashie_inspect
|
41
|
+
ret = "<##{self.class.to_s}"
|
42
|
+
stringify_keys.keys.sort.each do |key|
|
43
|
+
ret << " #{key}=#{self[key].inspect}"
|
44
|
+
end
|
45
|
+
ret << ">"
|
46
|
+
ret
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
module Hashie
|
2
|
+
# Mash allows you to create pseudo-objects that have method-like
|
3
|
+
# accessors for hash keys. This is useful for such implementations
|
4
|
+
# as an API-accessing library that wants to fake robust objects
|
5
|
+
# without the overhead of actually doing so. Think of it as OpenStruct
|
6
|
+
# with some additional goodies.
|
7
|
+
#
|
8
|
+
# A Mash will look at the methods you pass it and perform operations
|
9
|
+
# based on the following rules:
|
10
|
+
#
|
11
|
+
# * No punctuation: Returns the value of the hash for that key, or nil if none exists.
|
12
|
+
# * Assignment (<tt>=</tt>): Sets the attribute of the given method name.
|
13
|
+
# * Existence (<tt>?</tt>): Returns true or false depending on whether that key has been set.
|
14
|
+
# * Bang (<tt>!</tt>): Forces the existence of this key, used for deep Mashes. Think of it as "touch" for mashes.
|
15
|
+
#
|
16
|
+
# == Basic Example
|
17
|
+
#
|
18
|
+
# mash = Mash.new
|
19
|
+
# mash.name? # => false
|
20
|
+
# mash.name = "Bob"
|
21
|
+
# mash.name # => "Bob"
|
22
|
+
# mash.name? # => true
|
23
|
+
#
|
24
|
+
# == Hash Conversion Example
|
25
|
+
#
|
26
|
+
# hash = {:a => {:b => 23, :d => {:e => "abc"}}, :f => [{:g => 44, :h => 29}, 12]}
|
27
|
+
# mash = Mash.new(hash)
|
28
|
+
# mash.a.b # => 23
|
29
|
+
# mash.a.d.e # => "abc"
|
30
|
+
# mash.f.first.g # => 44
|
31
|
+
# mash.f.last # => 12
|
32
|
+
#
|
33
|
+
# == Bang Example
|
34
|
+
#
|
35
|
+
# mash = Mash.new
|
36
|
+
# mash.author # => nil
|
37
|
+
# mash.author! # => <Mash>
|
38
|
+
#
|
39
|
+
# mash = Mash.new
|
40
|
+
# mash.author!.name = "Michael Bleigh"
|
41
|
+
# mash.author # => <Mash name="Michael Bleigh">
|
42
|
+
#
|
43
|
+
class Mash < Hashie::Hash
|
44
|
+
include Hashie::PrettyInspect
|
45
|
+
alias_method :to_s, :inspect
|
46
|
+
|
47
|
+
# If you pass in an existing hash, it will
|
48
|
+
# convert it to a Mash including recursively
|
49
|
+
# descending into arrays and hashes, converting
|
50
|
+
# them as well.
|
51
|
+
def initialize(source_hash = nil, default = nil, &blk)
|
52
|
+
deep_update(source_hash) if source_hash
|
53
|
+
default ? super(default) : super(&blk)
|
54
|
+
end
|
55
|
+
|
56
|
+
class << self; alias [] new; end
|
57
|
+
|
58
|
+
def id #:nodoc:
|
59
|
+
key?("id") ? self["id"] : super
|
60
|
+
end
|
61
|
+
|
62
|
+
alias_method :regular_reader, :[]
|
63
|
+
alias_method :regular_writer, :[]=
|
64
|
+
|
65
|
+
# Retrieves an attribute set in the Mash. Will convert
|
66
|
+
# any key passed in to a string before retrieving.
|
67
|
+
def [](key)
|
68
|
+
regular_reader(convert_key(key))
|
69
|
+
end
|
70
|
+
|
71
|
+
# Sets an attribute in the Mash. Key will be converted to
|
72
|
+
# a string before it is set, and Hashes will be converted
|
73
|
+
# into Mashes for nesting purposes.
|
74
|
+
def []=(key,value) #:nodoc:
|
75
|
+
regular_writer(convert_key(key), convert_value(value))
|
76
|
+
end
|
77
|
+
|
78
|
+
# This is the bang method reader, it will return a new Mash
|
79
|
+
# if there isn't a value already assigned to the key requested.
|
80
|
+
def initializing_reader(key)
|
81
|
+
ck = convert_key(key)
|
82
|
+
regular_writer(ck, Hashie::Mash.new) unless key?(ck)
|
83
|
+
regular_reader(ck)
|
84
|
+
end
|
85
|
+
|
86
|
+
alias_method :regular_dup, :dup
|
87
|
+
# Duplicates the current mash as a new mash.
|
88
|
+
def dup
|
89
|
+
Mash.new(self, self.default)
|
90
|
+
end
|
91
|
+
|
92
|
+
def key?(key)
|
93
|
+
super(convert_key(key))
|
94
|
+
end
|
95
|
+
|
96
|
+
# Performs a deep_update on a duplicate of the
|
97
|
+
# current mash.
|
98
|
+
def deep_merge(other_hash)
|
99
|
+
dup.deep_merge!(other_hash)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Recursively merges this mash with the passed
|
103
|
+
# in hash, merging each hash in the hierarchy.
|
104
|
+
def deep_update(other_hash)
|
105
|
+
other_hash.each_pair do |k,v|
|
106
|
+
regular_writer(convert_key(k), convert_value(other_hash[k], true))
|
107
|
+
end
|
108
|
+
self
|
109
|
+
end
|
110
|
+
alias_method :deep_merge!, :deep_update
|
111
|
+
alias_method :update, :deep_update
|
112
|
+
alias_method :merge!, :update
|
113
|
+
|
114
|
+
|
115
|
+
def method_missing(method_name, *args, &blk)
|
116
|
+
return self[method_name] if key?(method_name)
|
117
|
+
match = method_name.to_s.match(/(.*?)([?=!]?)$/)
|
118
|
+
case match[2]
|
119
|
+
when "="
|
120
|
+
self[match[1]] = args.first
|
121
|
+
when "?"
|
122
|
+
key?(match[1])
|
123
|
+
when "!"
|
124
|
+
initializing_reader(match[1])
|
125
|
+
else
|
126
|
+
default(method_name, *args, &blk)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
protected
|
131
|
+
|
132
|
+
def convert_key(key) #:nodoc:
|
133
|
+
key.to_s
|
134
|
+
end
|
135
|
+
|
136
|
+
def convert_value(val, duping=false) #:nodoc:
|
137
|
+
case val
|
138
|
+
when ::Hash
|
139
|
+
val = val.dup if duping
|
140
|
+
self.class.new(val)
|
141
|
+
when Array
|
142
|
+
val.collect{ |e| convert_value(e) }
|
143
|
+
else
|
144
|
+
val
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|