wahashie 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 258e46712a6b4ef449c044714d29ad3ba3ea6188
4
+ data.tar.gz: 276f1cc363c7b4b9b842c7a6edee29663507c479
5
+ SHA512:
6
+ metadata.gz: bbcb4e8a70d26344547085d4a4a07fdc730899e9b8481ddefaa0541c193558dff9e2b0baf278359e9de3db8a5e5b78fc3679024568e8bf2a76a162df383f0003
7
+ data.tar.gz: de962728e1af3e83e9bc5bab255de03d00882c27b2c9d0e1d2675e8c5451ffa31f67b6cd4eb5488143ac6867925ce27c533a5dca20ac481e8cc1b275e2ea9728
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
6
+ *.gem
7
+ .bundle
8
+ .rvmrc
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format=nested
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - rbx
5
+ - ree
6
+ - ruby-head
7
+ - jruby
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ -m markdown
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'http://rubygems.org'
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,37 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ wahashie (1.2.0)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.1.2)
10
+ guard (0.5.1)
11
+ thor (~> 0.14.6)
12
+ guard-rspec (0.4.0)
13
+ guard (>= 0.4.0)
14
+ rake (0.9.2)
15
+ rspec (2.6.0)
16
+ rspec-core (~> 2.6.0)
17
+ rspec-expectations (~> 2.6.0)
18
+ rspec-mocks (~> 2.6.0)
19
+ rspec-core (2.6.4)
20
+ rspec-expectations (2.6.0)
21
+ diff-lcs (~> 1.1.2)
22
+ rspec-mocks (2.6.0)
23
+ thor (0.14.6)
24
+
25
+ PLATFORMS
26
+ java
27
+ ruby
28
+
29
+ DEPENDENCIES
30
+ guard
31
+ guard-rspec
32
+ rake (~> 0.9.2)
33
+ rspec (~> 2.5)
34
+ wahashie!
35
+
36
+ BUNDLED WITH
37
+ 1.15.4
data/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ guard 'rspec' do
2
+ watch(%r{^spec/.+_spec\.rb})
3
+ watch(%r{^lib/(.+)\.rb}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Intridea, Inc.
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.rdoc ADDED
@@ -0,0 +1,120 @@
1
+ = Wahashie
2
+
3
+ Wahashie is a growing collection of tools that extend Hashes and make
4
+ them more useful.
5
+
6
+ == Installation
7
+
8
+ Wahashie is available as a RubyGem:
9
+
10
+ gem install wahashie
11
+
12
+ == Mash
13
+
14
+ Mash is an extended Hash that gives simple pseudo-object functionality
15
+ that can be built from hashes and easily extended. It is designed to
16
+ be used in RESTful API libraries to provide easy object-like access
17
+ to JSON and XML parsed hashes.
18
+
19
+ === Example:
20
+
21
+ mash = Wahashie::Mash.new
22
+ mash.name? # => false
23
+ mash.name # => nil
24
+ mash.name = "My Mash"
25
+ mash.name # => "My Mash"
26
+ mash.name? # => true
27
+ mash.inspect # => <Wahashie::Mash name="My Mash">
28
+
29
+ mash = Mash.new
30
+ # use bang methods for multi-level assignment
31
+ mash.author!.name = "Michael Bleigh"
32
+ mash.author # => <Wahashie::Mash name="Michael Bleigh">
33
+
34
+ <b>Note:</b> The <tt>?</tt> method will return false if a key has been set
35
+ to false or nil. In order to check if a key has been set at all, use the
36
+ <tt>mash.key?('some_key')</tt> method instead.
37
+
38
+ == Dash
39
+
40
+ Dash is an extended Hash that has a discrete set of defined properties
41
+ and only those properties may be set on the hash. Additionally, you
42
+ can set defaults for each property. You can also flag a property as
43
+ required. Required properties will raise an execption if unset.
44
+
45
+ === Example:
46
+
47
+ class Person < Wahashie::Dash
48
+ property :name, :required => true
49
+ property :email
50
+ property :occupation, :default => 'Rubyist'
51
+ end
52
+
53
+ p = Person.new # => ArgumentError: The property 'name' is required for this Dash.
54
+
55
+ p = Person.new(:name => "Bob")
56
+ p.name # => 'Bob'
57
+ p.name = nil # => ArgumentError: The property 'name' is required for this Dash.
58
+ p.email = 'abc@def.com'
59
+ p.occupation # => 'Rubyist'
60
+ p.email # => 'abc@def.com'
61
+ p[:awesome] # => NoMethodError
62
+ p[:occupation] # => 'Rubyist'
63
+
64
+ == Trash
65
+
66
+ A Trash is a Dash that allows you to translate keys on initialization.
67
+ It is used like so:
68
+
69
+ class Person < Wahashie::Trash
70
+ property :first_name, :from => :firstName
71
+ end
72
+
73
+ This will automatically translate the <tt>firstName</tt> key to <tt>first_name</tt>
74
+ when it is initialized using a hash such as through:
75
+
76
+ Person.new(:firstName => 'Bob')
77
+
78
+ == Clash
79
+
80
+ Clash is a Chainable Lazy Hash that allows you to easily construct
81
+ complex hashes using method notation chaining. This will allow you
82
+ to use a more action-oriented approach to building options hashes.
83
+
84
+ Essentially, a Clash is a generalized way to provide much of the same
85
+ kind of "chainability" that libraries like Arel or Rails 2.x's named_scopes
86
+ provide.
87
+
88
+ === Example
89
+
90
+ c = Wahashie::Clash.new
91
+ c.where(:abc => 'def').order(:created_at)
92
+ c # => {:where => {:abc => 'def}, :order => :created_at}
93
+
94
+ # You can also use bang notation to chain into sub-hashes,
95
+ # jumping back up the chain with _end!
96
+ c = Wahashie::Clash.new
97
+ c.where!.abc('def').ghi(123)._end!.order(:created_at)
98
+ c # => {:where => {:abc => 'def', :ghi => 123}, :order => :created_at}
99
+
100
+ # Multiple hashes are merged automatically
101
+ c = Wahashie::Clash.new
102
+ c.where(:abc => 'def').where(:hgi => 123)
103
+ c # => {:where => {:abc => 'def', :hgi => 123}}
104
+
105
+
106
+ == Note on Patches/Pull Requests
107
+
108
+ * Fork the project.
109
+ * Make your feature addition or bug fix.
110
+ * Add tests for it. This is important so I don't break it in a future version unintentionally.
111
+ * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
112
+ * Send me a pull request. Bonus points for topic branches.
113
+
114
+ == Authors
115
+
116
+ * Michael Bleigh
117
+
118
+ == Copyright
119
+
120
+ Copyright (c) 2009 Intridea, Inc (http://intridea.com/). See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup
4
+
5
+ Bundler::GemHelper.install_tasks
6
+
7
+ require 'rspec/core/rake_task'
8
+ RSpec::Core::RakeTask.new do |spec|
9
+ # spec.libs << 'lib' << 'spec'
10
+ spec.pattern = 'spec/**/*_spec.rb'
11
+ end
12
+
13
+ task :default => :spec
data/lib/wahashie.rb ADDED
@@ -0,0 +1,9 @@
1
+ module Wahashie
2
+ autoload :HashExtensions, 'wahashie/hash_extensions'
3
+ autoload :PrettyInspect, 'wahashie/hash_extensions'
4
+ autoload :Hash, 'wahashie/hash'
5
+ autoload :Trash, 'wahashie/trash'
6
+ autoload :Mash, 'wahashie/mash'
7
+ autoload :Dash, 'wahashie/dash'
8
+ autoload :Clash, 'wahashie/clash'
9
+ end
@@ -0,0 +1,86 @@
1
+ require 'wahashie/hash'
2
+
3
+ module Wahashie
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 = Wahashie::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 = Wahashie::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 = Wahashie::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,150 @@
1
+ require 'wahashie/hash'
2
+ require 'set'
3
+
4
+ module Wahashie
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 < Wahashie::Hash
16
+ include Wahashie::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
+ # * <tt>:required</tt> - Specify the value as required for this
27
+ # property, to raise an error if a value is unset in a new or
28
+ # existing Dash.
29
+ #
30
+ def self.property(property_name, options = {})
31
+ property_name = property_name.to_sym
32
+
33
+ self.properties << property_name
34
+
35
+ if options.has_key?(:default)
36
+ self.defaults[property_name] = options[:default]
37
+ elsif self.defaults.has_key?(property_name)
38
+ self.defaults.delete property_name
39
+ end
40
+
41
+ unless instance_methods.map { |m| m.to_s }.include?("#{property_name}=")
42
+ class_eval <<-ACCESSORS
43
+ def #{property_name}(&block)
44
+ self.[](#{property_name.to_s.inspect}, &block)
45
+ end
46
+
47
+ def #{property_name}=(value)
48
+ self.[]=(#{property_name.to_s.inspect}, value)
49
+ end
50
+ ACCESSORS
51
+ end
52
+
53
+ if defined? @subclasses
54
+ @subclasses.each { |klass| klass.property(property_name, options) }
55
+ end
56
+ required_properties << property_name if options.delete(:required)
57
+ end
58
+
59
+ class << self
60
+ attr_reader :properties, :defaults
61
+ attr_reader :required_properties
62
+ end
63
+ instance_variable_set('@properties', Set.new)
64
+ instance_variable_set('@defaults', {})
65
+ instance_variable_set('@required_properties', Set.new)
66
+
67
+ def self.inherited(klass)
68
+ super
69
+ (@subclasses ||= Set.new) << klass
70
+ klass.instance_variable_set('@properties', self.properties.dup)
71
+ klass.instance_variable_set('@defaults', self.defaults.dup)
72
+ klass.instance_variable_set('@required_properties', self.required_properties.dup)
73
+ end
74
+
75
+ # Check to see if the specified property has already been
76
+ # defined.
77
+ def self.property?(name)
78
+ properties.include? name.to_sym
79
+ end
80
+
81
+ # Check to see if the specified property is
82
+ # required.
83
+ def self.required?(name)
84
+ required_properties.include? name.to_sym
85
+ end
86
+
87
+ # You may initialize a Dash with an attributes hash
88
+ # just like you would many other kinds of data objects.
89
+ def initialize(attributes = {}, &block)
90
+ super(&block)
91
+
92
+ self.class.defaults.each_pair do |prop, value|
93
+ self[prop] = value
94
+ end
95
+
96
+ attributes.each_pair do |att, value|
97
+ self[att] = value
98
+ end if attributes
99
+ assert_required_properties_set!
100
+ end
101
+
102
+ alias_method :_regular_reader, :[]
103
+ alias_method :_regular_writer, :[]=
104
+ private :_regular_reader, :_regular_writer
105
+
106
+ # Retrieve a value from the Dash (will return the
107
+ # property's default value if it hasn't been set).
108
+ def [](property)
109
+ assert_property_exists! property
110
+ value = super(property.to_s)
111
+ yield value if block_given?
112
+ value
113
+ end
114
+
115
+ # Set a value on the Dash in a Hash-like way. Only works
116
+ # on pre-existing properties.
117
+ def []=(property, value)
118
+ assert_property_required! property, value
119
+ assert_property_exists! property
120
+ super(property.to_s, value)
121
+ end
122
+
123
+ private
124
+
125
+ def assert_property_exists!(property)
126
+ unless self.class.property?(property)
127
+ raise NoMethodError, "The property '#{property}' is not defined for this Dash."
128
+ end
129
+ end
130
+
131
+ def assert_required_properties_set!
132
+ self.class.required_properties.each do |required_property|
133
+ assert_property_set!(required_property)
134
+ end
135
+ end
136
+
137
+ def assert_property_set!(property)
138
+ if send(property).nil?
139
+ raise ArgumentError, "The property '#{property}' is required for this Dash."
140
+ end
141
+ end
142
+
143
+ def assert_property_required!(property, value)
144
+ if self.class.required?(property) && value.nil?
145
+ raise ArgumentError, "The property '#{property}' is required for this Dash."
146
+ end
147
+ end
148
+
149
+ end
150
+ end