hashie-model 1.0.0.alpha
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/LICENSE.txt +20 -0
- data/README.md +3 -0
- data/hashie-model.gemspec +106 -0
- data/lib/hashie-model.rb +28 -0
- data/lib/hashie_model/array_of.rb +15 -0
- data/lib/hashie_model/associated_validator.rb +8 -0
- data/lib/hashie_model/base.rb +74 -0
- data/lib/hashie_model/money.rb +12 -0
- data/lib/hashie_model/spec/matchers.rb +86 -0
- data/lib/hashie_model/version.rb +10 -0
- data/vendor/hashie/lib/hashie.rb +23 -0
- data/vendor/hashie/lib/hashie/clash.rb +86 -0
- data/vendor/hashie/lib/hashie/dash.rb +150 -0
- data/vendor/hashie/lib/hashie/extensions/coercion.rb +101 -0
- data/vendor/hashie/lib/hashie/extensions/deep_merge.rb +7 -0
- data/vendor/hashie/lib/hashie/extensions/indifferent_access.rb +110 -0
- data/vendor/hashie/lib/hashie/extensions/key_conversion.rb +52 -0
- data/vendor/hashie/lib/hashie/extensions/merge_initializer.rb +24 -0
- data/vendor/hashie/lib/hashie/extensions/method_access.rb +124 -0
- data/vendor/hashie/lib/hashie/extensions/structure.rb +47 -0
- data/vendor/hashie/lib/hashie/hash.rb +24 -0
- data/vendor/hashie/lib/hashie/hash_extensions.rb +49 -0
- data/vendor/hashie/lib/hashie/mash.rb +191 -0
- data/vendor/hashie/lib/hashie/trash.rb +55 -0
- data/vendor/hashie/lib/hashie/version.rb +3 -0
- metadata +241 -0
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Istvan Hoka
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{hashie-model}
|
8
|
+
s.version = "1.0.0.alpha"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = [%q{ZenCash.com}]
|
12
|
+
s.date = %q{2011-11-01}
|
13
|
+
s.description = %q{Hashie + ActiveModel 3, offering declared properties, validations, JSON serialization/deserialization}
|
14
|
+
s.email = %q{istvan@zencash.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
"LICENSE.txt",
|
21
|
+
"README.md",
|
22
|
+
"hashie-model.gemspec",
|
23
|
+
"lib/hashie-model.rb",
|
24
|
+
"lib/hashie_model/array_of.rb",
|
25
|
+
"lib/hashie_model/associated_validator.rb",
|
26
|
+
"lib/hashie_model/base.rb",
|
27
|
+
"lib/hashie_model/money.rb",
|
28
|
+
"lib/hashie_model/spec/matchers.rb",
|
29
|
+
"lib/hashie_model/version.rb",
|
30
|
+
"vendor/hashie/lib/hashie.rb",
|
31
|
+
"vendor/hashie/lib/hashie/clash.rb",
|
32
|
+
"vendor/hashie/lib/hashie/dash.rb",
|
33
|
+
"vendor/hashie/lib/hashie/extensions/coercion.rb",
|
34
|
+
"vendor/hashie/lib/hashie/extensions/deep_merge.rb",
|
35
|
+
"vendor/hashie/lib/hashie/extensions/indifferent_access.rb",
|
36
|
+
"vendor/hashie/lib/hashie/extensions/key_conversion.rb",
|
37
|
+
"vendor/hashie/lib/hashie/extensions/merge_initializer.rb",
|
38
|
+
"vendor/hashie/lib/hashie/extensions/method_access.rb",
|
39
|
+
"vendor/hashie/lib/hashie/extensions/structure.rb",
|
40
|
+
"vendor/hashie/lib/hashie/hash.rb",
|
41
|
+
"vendor/hashie/lib/hashie/hash_extensions.rb",
|
42
|
+
"vendor/hashie/lib/hashie/mash.rb",
|
43
|
+
"vendor/hashie/lib/hashie/trash.rb",
|
44
|
+
"vendor/hashie/lib/hashie/version.rb"
|
45
|
+
]
|
46
|
+
s.homepage = %q{http://github.com/doublewide/hashie-model}
|
47
|
+
s.licenses = [%q{MIT}]
|
48
|
+
s.require_paths = [%q{lib}]
|
49
|
+
s.rubygems_version = %q{1.8.6}
|
50
|
+
s.summary = %q{Hashie + ActiveModel 3.x}
|
51
|
+
|
52
|
+
if s.respond_to? :specification_version then
|
53
|
+
s.specification_version = 3
|
54
|
+
|
55
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
56
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 3.1.1"])
|
57
|
+
s.add_runtime_dependency(%q<activemodel>, [">= 3.1.1"])
|
58
|
+
s.add_runtime_dependency(%q<money>, [">= 3.7.1"])
|
59
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
|
60
|
+
s.add_development_dependency(%q<shoulda-matchers>, ["~> 1.0.0.beta3"])
|
61
|
+
s.add_development_dependency(%q<yard>, [">= 0.7.0"])
|
62
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
63
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
|
64
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
65
|
+
s.add_development_dependency(%q<rb-fsevent>, [">= 0"])
|
66
|
+
s.add_development_dependency(%q<growl>, [">= 0"])
|
67
|
+
s.add_development_dependency(%q<guard>, [">= 0"])
|
68
|
+
s.add_development_dependency(%q<guard-rspec>, [">= 0"])
|
69
|
+
s.add_development_dependency(%q<guard-yard>, [">= 0"])
|
70
|
+
s.add_development_dependency(%q<guard-bundler>, [">= 0"])
|
71
|
+
else
|
72
|
+
s.add_dependency(%q<activesupport>, [">= 3.1.1"])
|
73
|
+
s.add_dependency(%q<activemodel>, [">= 3.1.1"])
|
74
|
+
s.add_dependency(%q<money>, [">= 3.7.1"])
|
75
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
76
|
+
s.add_dependency(%q<shoulda-matchers>, ["~> 1.0.0.beta3"])
|
77
|
+
s.add_dependency(%q<yard>, [">= 0.7.0"])
|
78
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
79
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
80
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
81
|
+
s.add_dependency(%q<rb-fsevent>, [">= 0"])
|
82
|
+
s.add_dependency(%q<growl>, [">= 0"])
|
83
|
+
s.add_dependency(%q<guard>, [">= 0"])
|
84
|
+
s.add_dependency(%q<guard-rspec>, [">= 0"])
|
85
|
+
s.add_dependency(%q<guard-yard>, [">= 0"])
|
86
|
+
s.add_dependency(%q<guard-bundler>, [">= 0"])
|
87
|
+
end
|
88
|
+
else
|
89
|
+
s.add_dependency(%q<activesupport>, [">= 3.1.1"])
|
90
|
+
s.add_dependency(%q<activemodel>, [">= 3.1.1"])
|
91
|
+
s.add_dependency(%q<money>, [">= 3.7.1"])
|
92
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
93
|
+
s.add_dependency(%q<shoulda-matchers>, ["~> 1.0.0.beta3"])
|
94
|
+
s.add_dependency(%q<yard>, [">= 0.7.0"])
|
95
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
96
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
97
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
98
|
+
s.add_dependency(%q<rb-fsevent>, [">= 0"])
|
99
|
+
s.add_dependency(%q<growl>, [">= 0"])
|
100
|
+
s.add_dependency(%q<guard>, [">= 0"])
|
101
|
+
s.add_dependency(%q<guard-rspec>, [">= 0"])
|
102
|
+
s.add_dependency(%q<guard-yard>, [">= 0"])
|
103
|
+
s.add_dependency(%q<guard-bundler>, [">= 0"])
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
data/lib/hashie-model.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'active_model'
|
2
|
+
|
3
|
+
# Load vendored Hashie unless it is already loaded.
|
4
|
+
# We must have version >= 2.0 in order to get the Conversions API.
|
5
|
+
if defined?(Hashie)
|
6
|
+
raise "Hashie >= 2.0.0 required by HashieModel." unless Hashie::VERSION.split('.').first.to_i >= 2
|
7
|
+
else
|
8
|
+
$:.unshift File.expand_path(File.join('..', 'vendor', 'hashie', 'lib'), File.dirname(__FILE__))
|
9
|
+
end
|
10
|
+
require 'hashie'
|
11
|
+
|
12
|
+
require 'active_support'
|
13
|
+
require 'active_support/core_ext'
|
14
|
+
require 'active_support/json'
|
15
|
+
require 'money'
|
16
|
+
|
17
|
+
module HashieModel
|
18
|
+
extend ActiveSupport::Autoload
|
19
|
+
|
20
|
+
autoload :ArrayOf
|
21
|
+
autoload :AssociatedValidator
|
22
|
+
autoload :Base
|
23
|
+
autoload :Money
|
24
|
+
|
25
|
+
autoload :Version
|
26
|
+
|
27
|
+
include ArrayOf
|
28
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module HashieModel
|
2
|
+
module ArrayOf
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def array_of(klass)
|
7
|
+
Class.new(Array) do
|
8
|
+
define_method(:initialize) do |values|
|
9
|
+
replace values.map { |value| klass.new(value) }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
module HashieModel
|
2
|
+
class AssociatedValidator < ActiveModel::EachValidator
|
3
|
+
def validate_each(record, attribute, value)
|
4
|
+
return if (value.is_a?(Array) ? value : [value]).collect{ |r| r.nil? || r.valid? }.all?
|
5
|
+
record.errors.add(attribute, :invalid, options.merge(:value => value))
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module HashieModel
|
2
|
+
class Base < Hashie::Dash
|
3
|
+
extend ActiveModel::Naming
|
4
|
+
|
5
|
+
include Hashie::Extensions::Coercion
|
6
|
+
|
7
|
+
include ActiveModel::AttributeMethods
|
8
|
+
include ActiveModel::Conversion
|
9
|
+
include ActiveModel::Validations
|
10
|
+
|
11
|
+
attribute_method_suffix '?', '_before_type_cast'
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def prop(name, type = nil, options = {})
|
15
|
+
if type.is_a?(Hash)
|
16
|
+
options = type
|
17
|
+
type = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
property(name, options)
|
21
|
+
|
22
|
+
if type
|
23
|
+
coerce_key(name.to_sym, type)
|
24
|
+
coerce_key(name.to_s, type)
|
25
|
+
end
|
26
|
+
|
27
|
+
define_attribute_methods [name]
|
28
|
+
alias_method :"#{name}_without_type_cast=", :"#{name}="
|
29
|
+
|
30
|
+
define_method(:"#{name}=") do |value|
|
31
|
+
attributes_before_type_cast[name.to_s] = value
|
32
|
+
send(:"#{name}_without_type_cast=", value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def from_json(json)
|
37
|
+
new(ActiveSupport::JSON.decode(json))
|
38
|
+
end
|
39
|
+
|
40
|
+
def validates_associated(*attr_names)
|
41
|
+
validates_with HashieModel::AssociatedValidator, _merge_attributes(attr_names)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def attributes_before_type_cast
|
46
|
+
@attributes_before_type_cast ||= {}
|
47
|
+
end
|
48
|
+
|
49
|
+
def attributes
|
50
|
+
{}.tap do |attrs|
|
51
|
+
self.class.properties.each do |key|
|
52
|
+
attrs[key] = self[key]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def persisted?
|
58
|
+
false
|
59
|
+
end
|
60
|
+
|
61
|
+
protected
|
62
|
+
def attribute?(key)
|
63
|
+
self[key].present?
|
64
|
+
end
|
65
|
+
|
66
|
+
def reset_attribute(key)
|
67
|
+
self[key] = self.class.defaults[key.to_sym]
|
68
|
+
end
|
69
|
+
|
70
|
+
def attribute_before_type_cast(key)
|
71
|
+
attributes_before_type_cast[key.to_s]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
RSpec::Matchers.define :have_property do |*expected|
|
2
|
+
options = expected.extract_options!
|
3
|
+
property_name = expected.first
|
4
|
+
coercion = expected.second
|
5
|
+
|
6
|
+
description {
|
7
|
+
desc = "have"
|
8
|
+
desc << " required" if options[:required]
|
9
|
+
desc << " property :#{property_name}"
|
10
|
+
desc << ", coerced to #{coercion}" if coercion
|
11
|
+
desc << ", with default '#{options[:default]}'" if options.has_key?(:default)
|
12
|
+
desc
|
13
|
+
}
|
14
|
+
|
15
|
+
match do |actual|
|
16
|
+
property_exists = actual.class.properties.include?(property_name)
|
17
|
+
|
18
|
+
default_matches = if options.has_key?(:default)
|
19
|
+
actual.class.defaults[property_name] == options[:default]
|
20
|
+
else
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
required_matches = if options[:required]
|
25
|
+
actual.class.required_properties.include?(property_name)
|
26
|
+
else
|
27
|
+
!actual.class.required_properties.include?(property_name)
|
28
|
+
end
|
29
|
+
|
30
|
+
coercion_matches = if coercion
|
31
|
+
actual.class.key_coercion(property_name.to_sym) == coercion &&
|
32
|
+
actual.class.key_coercion(property_name.to_s) == coercion
|
33
|
+
else
|
34
|
+
actual.class.key_coercion(property_name.to_sym).nil? &&
|
35
|
+
actual.class.key_coercion(property_name.to_s).nil?
|
36
|
+
end
|
37
|
+
|
38
|
+
property_exists && default_matches && required_matches && coercion_matches
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
RSpec::Matchers.define :serialize_to do |json|
|
43
|
+
description { "serialize to JSON" }
|
44
|
+
expected = ActiveSupport::JSON.decode(json)
|
45
|
+
|
46
|
+
match do |object|
|
47
|
+
actual = ActiveSupport::JSON.decode(object.to_json)
|
48
|
+
actual == expected
|
49
|
+
end
|
50
|
+
|
51
|
+
failure_message_for_should do |object|
|
52
|
+
actual = ActiveSupport::JSON.decode(object.to_json)
|
53
|
+
"expected that #{object} would serialize to #{expected.pretty_inspect}Diff:#{RSpec::Expectations.differ.diff_as_object(actual, expected)}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
RSpec::Matchers.define :deserialize_from do |json|
|
58
|
+
description { "deserialize from JSON" }
|
59
|
+
|
60
|
+
match do |object|
|
61
|
+
new_object = object.class.from_json(json)
|
62
|
+
new_object.is_a?(object.class) && new_object == object
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
RSpec::Matchers.define :act_as_array do
|
67
|
+
match do |object|
|
68
|
+
klass = object.class
|
69
|
+
array_klass = ZCQ::Component.const_get(:"ArrayOf#{klass.name.demodulize}s")
|
70
|
+
|
71
|
+
actual = [object]
|
72
|
+
expected = array_klass.new([object.as_json])
|
73
|
+
|
74
|
+
actual == expected
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
RSpec::Matchers.define :validate_associated do |attribute|
|
79
|
+
match do |component|
|
80
|
+
component.stub(attribute => mock("#{component.class}.#{attribute}", :valid? => false))
|
81
|
+
is_invalid = !component.valid?
|
82
|
+
right_error_count = component.errors[attribute].size == 1
|
83
|
+
|
84
|
+
is_invalid && right_error_count
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Hashie
|
2
|
+
autoload :Clash, 'hashie/clash'
|
3
|
+
autoload :Dash, 'hashie/dash'
|
4
|
+
autoload :Hash, 'hashie/hash'
|
5
|
+
autoload :HashExtensions, 'hashie/hash_extensions'
|
6
|
+
autoload :Mash, 'hashie/mash'
|
7
|
+
autoload :PrettyInspect, 'hashie/hash_extensions'
|
8
|
+
autoload :Trash, 'hashie/trash'
|
9
|
+
|
10
|
+
module Extensions
|
11
|
+
autoload :Coercion, 'hashie/extensions/coercion'
|
12
|
+
autoload :DeepMerge, 'hashie/extensions/deep_merge'
|
13
|
+
autoload :KeyConversion, 'hashie/extensions/key_conversion'
|
14
|
+
autoload :IndifferentAccess, 'hashie/extensions/indifferent_access'
|
15
|
+
autoload :MergeInitializer, 'hashie/extensions/merge_initializer'
|
16
|
+
autoload :MethodAccess, 'hashie/extensions/method_access'
|
17
|
+
autoload :MethodQuery, 'hashie/extensions/method_access'
|
18
|
+
autoload :MethodReader, 'hashie/extensions/method_access'
|
19
|
+
autoload :MethodWriter, 'hashie/extensions/method_access'
|
20
|
+
autoload :StringifyKeys, 'hashie/extensions/key_conversion'
|
21
|
+
autoload :SymbolizeKeys, 'hashie/extensions/key_conversion'
|
22
|
+
end
|
23
|
+
end
|
@@ -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
|