pancake 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|