deepstruct 0.0.1

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/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in deepstruct.gemspec
4
+ gemspec
data/README.markdown ADDED
@@ -0,0 +1,62 @@
1
+ Deepstruct
2
+ ==========
3
+
4
+ An adapter that wraps common ruby data-structures and makes them look like proper Objects.
5
+
6
+ struct = DeepStruct.wrap({"a" => { "b" => "bingo!"}})
7
+ struct.a.b
8
+ => "bingo!"
9
+
10
+
11
+ Installation
12
+ ============
13
+
14
+ Not yet published as a gem, so you'll have to
15
+
16
+ git clone git@github.com:simen/deepstruct.git
17
+ cd deepstruct
18
+ rake install
19
+
20
+ Or if you use the awesomeness that is bundler, you stick this in your Gemfile:
21
+
22
+ gem "deepstruct", :git => git@github.com:simen/deepstruct.git
23
+
24
+ Usage
25
+ =====
26
+
27
+ struct = DeepStruct.wrap({:awesome => [1,2,3, {"a" => "hello from the abyss"}]})
28
+ struct.awesome[3].a
29
+ => "hello from the abyss"
30
+
31
+ You can also write back through the wrapper with indifferent access
32
+
33
+ struct = DeepStruct.wrap({:a => 1, "b" => 2})
34
+ struct.a = 10
35
+ struct.b = 20
36
+ struct
37
+ => #<DeepStruct::HashWrapper {:a=>10, "b"=>20}>
38
+
39
+ When you want to you can still use hash-style syntax when accessing your DeepStructs. These accessors implement indifferent access too.
40
+
41
+ struct = DeepStruct.wrap({:a => {1 => 'One', 2 => 'Two'}})
42
+ struct.a[1]
43
+ => "One"
44
+ struct["a"][1]
45
+ => "One"
46
+ struct["b"] = "Hello"
47
+ struct[:b]
48
+ => "Hello"
49
+ struct.b
50
+ => "Hello"
51
+
52
+ If DeepStruct is getting in your way, you can always get access to the raw content by unwrapping it:
53
+
54
+ struct = Deepstruct.wrap({"hello => "world"})
55
+ struct.unwrap
56
+ => {"hello" => "world"}
57
+
58
+ DeepStruct is a perfect companion to your json-oriented application!
59
+
60
+ struct = DeepStruct.wrap(JSON.parse('{"full_name": "Don Corleone"}'))
61
+ puts struct.to_json
62
+ {"full_name":"Don Corleone"}
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "deepstruct/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "deepstruct"
7
+ s.version = Deepstruct::VERSION
8
+ s.authors = ["Simen Svale Skogsrud"]
9
+ s.email = ["simen@bengler.no"]
10
+ s.homepage = ""
11
+ s.summary = %q{A sibling of ostruct that handles deeply nested structures and basic data type detection}
12
+ s.description = %q{A sibling of ostruct that handles deeply nested structures and basic data type detection}
13
+
14
+ s.rubyforge_project = "deepstruct"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency "rspec"
22
+ end
@@ -0,0 +1,3 @@
1
+ module Deepstruct
2
+ VERSION = "0.0.1"
3
+ end
data/lib/deepstruct.rb ADDED
@@ -0,0 +1,94 @@
1
+ module DeepStruct
2
+ class DeepWrapper
3
+ def initialize(value)
4
+ @value = value
5
+ end
6
+
7
+ def unwrap
8
+ @value
9
+ end
10
+
11
+ def [](index)
12
+ return DeepStruct.wrap(@value[index])
13
+ end
14
+
15
+ def []=(index, value)
16
+ @value[index] = value
17
+ end
18
+
19
+ def inspect
20
+ "#<#{self.class} #{@value.inspect}>"
21
+ end
22
+
23
+ def to_json
24
+ @value.to_json
25
+ end
26
+ end
27
+
28
+ class HashWrapper < DeepWrapper
29
+ def respond_to?(method)
30
+ @value.respond_to?(method) || self.has_key?(method.to_s.chomp('='))
31
+ end
32
+
33
+ # Given a symbol or a string this yields the variant of the key that
34
+ # exists in the wrapped hash if any. If none exists (or the key is not
35
+ # a symbol or string) the input value is passed through unscathed.
36
+ def indiffrently(key, &block)
37
+ return yield(key) if @value.has_key?(key)
38
+ return yield(key.to_s) if key.is_a?(Symbol) && @value.has_key?(key.to_s)
39
+ return yield(key.to_sym) if key.is_a?(String) && @value.has_key?(key.to_sym)
40
+ return yield(key)
41
+ end
42
+
43
+ def []=(key, value)
44
+ indiffrently(key) { |key| @value[key] = value }
45
+ end
46
+
47
+ def [](key)
48
+ indiffrently(key) { |key| DeepStruct.wrap(@value[key]) }
49
+ end
50
+
51
+ def has_key?(key)
52
+ indiffrently(key) { |key| @value.has_key?(key) }
53
+ end
54
+
55
+ def method_missing(method, *args, &block)
56
+ return @value.send(method, *args, &block) if @value.respond_to?(method)
57
+ method = method.to_s
58
+ if method.chomp!('=')
59
+ raise ArgumentError, "wrong number of arguments (#{arg_count} for 1)", caller(1) if args.length != 1
60
+ self[method] = args[0]
61
+ elsif args.length == 0 && self.has_key?(method)
62
+ self[method]
63
+ else
64
+ raise NoMethodError, "undefined method `#{method}' for #{self}", caller(1)
65
+ end
66
+ end
67
+ end
68
+
69
+ class ArrayWrapper < DeepWrapper
70
+ include Enumerable
71
+
72
+ def each
73
+ block_given? or return enum_for(__method__)
74
+ @value.each { |o| yield(DeepStruct.wrap(o)) }
75
+ self
76
+ end
77
+
78
+ def size
79
+ @value.size
80
+ end
81
+ alias :length :size
82
+ end
83
+
84
+ def self.wrap(value)
85
+ case value
86
+ when Hash
87
+ return HashWrapper.new(value)
88
+ when Enumerable
89
+ return ArrayWrapper.new(value)
90
+ else
91
+ return value
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,119 @@
1
+ require 'spec_helper'
2
+ require 'json'
3
+
4
+ describe DeepStruct do
5
+ it "wraps a simple hash" do
6
+ struct = DeepStruct.wrap({:a => 1, :b => 2})
7
+ struct.a.should eq(1)
8
+ struct.b.should eq(2)
9
+ end
10
+
11
+ it "can unwrap a wrapped value" do
12
+ DeepStruct.wrap([1]).unwrap.should eq [1]
13
+ end
14
+
15
+ it "avoids wrapping common datatypes" do
16
+ DeepStruct.wrap("hello").should eq ("hello")
17
+ DeepStruct.wrap(1).should eq(1)
18
+ DeepStruct.wrap(1.0).should eq(1.0)
19
+ end
20
+
21
+ it "implements indifferenct access" do
22
+ struct = DeepStruct.wrap({:a => 1, "b" => 2})
23
+ struct.a.should eq(1)
24
+ struct.b.should eq(2)
25
+ end
26
+
27
+ it "writes back to the hash with the same key-type as the original hash" do
28
+ struct = DeepStruct.wrap({:a => 1, "b" => 2})
29
+ struct.a = 3
30
+ struct.b = 4
31
+ struct.a.should eq(3)
32
+ struct.b.should eq(4)
33
+ struct.keys.should_not include("a")
34
+ struct.keys.should_not include(:b)
35
+ end
36
+
37
+ it "wraps nested hashes" do
38
+ struct = DeepStruct.wrap({:a => {:b => {:c => "hello from deep space"}}})
39
+ struct.a.b.c.should eq("hello from deep space")
40
+ end
41
+
42
+ it "wraps arrays and support all common operations" do
43
+ struct = DeepStruct.wrap([1,2,3,4,5])
44
+ struct[3].should eq(4)
45
+ struct.sort{|a,b| b <=> a}.should eq([5,4,3,2,1])
46
+ struct.map(&:to_s).should eq(['1', '2', '3', '4', '5'])
47
+ end
48
+
49
+ it "wraps hashes nested within arrays" do
50
+ struct = DeepStruct.wrap([1, {:a => "hello"}])
51
+ struct[1].a.should eq('hello')
52
+ struct[1].class.should eq(DeepStruct::HashWrapper)
53
+ end
54
+
55
+ it "wraps arrays nested within hashes" do
56
+ struct = DeepStruct.wrap({:a => [1,2,3]})
57
+ struct.a[1].should eq(2)
58
+ struct.a.class.should eq(DeepStruct::ArrayWrapper)
59
+ end
60
+
61
+ it "supports being converted to json" do
62
+ struct = DeepStruct.wrap({:a => [1,2,3]})
63
+ struct.to_json.should eq('{"a":[1,2,3]}')
64
+ end
65
+
66
+ it "raises NoMethodError when reading missing keys" do
67
+ ->{DeepStruct.wrap({}).not_there}.should raise_error(NoMethodError)
68
+ end
69
+
70
+ context "HashWrapper, hash-like methods" do
71
+ it "can be used as a common ruby hash with indifferent access" do
72
+ struct = DeepStruct.wrap({:a => "hello", 'b' => "world"})
73
+ struct[:a].should eq "hello"
74
+ struct['a'].should eq "hello"
75
+ struct[:b].should eq "world"
76
+ struct['b'].should eq "world"
77
+ struct['a'] = "Mmkay"
78
+ struct[:a].should eq "Mmkay"
79
+ struct.has_key?("a").should be_true
80
+ end
81
+
82
+ it "can access non string/symbol elements via hashistic syntax" do
83
+ struct = DeepStruct.wrap({1 => "One"})
84
+ struct[1].should eq 'One'
85
+ struct[2] = "Two"
86
+ struct[2].should eq 'Two'
87
+ struct['2'].should eq nil
88
+ end
89
+ end
90
+
91
+ context "HashWrapper, #respond_to?" do
92
+ it "responds to hash methods" do
93
+ struct = DeepStruct.wrap({:a => true})
94
+ struct.size.should eq(1)
95
+ struct.respond_to?(:size).should be_true
96
+ end
97
+
98
+ it "responds to keys that are present as symbols" do
99
+ struct = DeepStruct.wrap({:a => nil})
100
+ struct.respond_to?(:a).should be_true
101
+ end
102
+
103
+ it "responds to keys that are present as strings" do
104
+ struct = DeepStruct.wrap({'a' => nil})
105
+ struct.respond_to?(:a).should be_true
106
+ end
107
+
108
+ it "doesn't respond to missing keys" do
109
+ struct = DeepStruct.wrap({:a => true})
110
+ struct.respond_to?(:b).should be_false
111
+ end
112
+
113
+ it "responds to keys that can be assigned to" do
114
+ struct = DeepStruct.wrap({:a => true})
115
+ struct.respond_to?(:a=).should be_true
116
+ end
117
+
118
+ end
119
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+ require 'delegate'
3
+
4
+ class PassingTheBuck < SimpleDelegator
5
+ end
6
+
7
+ describe DeepStruct do
8
+
9
+ specify "HashWrapper can be delegated to" do
10
+ buck = DeepStruct.wrap(:for_sure => 'hell, yeah!')
11
+
12
+ thing = PassingTheBuck.new(buck)
13
+ thing.for_sure.should eq('hell, yeah!')
14
+ end
15
+
16
+ end
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__)+'/../lib/deepstruct.rb'
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: deepstruct
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Simen Svale Skogsrud
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-10-29 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70116432443300 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70116432443300
25
+ description: A sibling of ostruct that handles deeply nested structures and basic
26
+ data type detection
27
+ email:
28
+ - simen@bengler.no
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - .gitignore
34
+ - Gemfile
35
+ - README.markdown
36
+ - Rakefile
37
+ - deepstruct.gemspec
38
+ - lib/deepstruct.rb
39
+ - lib/deepstruct/version.rb
40
+ - spec/deepstruct_spec.rb
41
+ - spec/delegation_spec.rb
42
+ - spec/spec_helper.rb
43
+ homepage: ''
44
+ licenses: []
45
+ post_install_message:
46
+ rdoc_options: []
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ requirements: []
62
+ rubyforge_project: deepstruct
63
+ rubygems_version: 1.8.10
64
+ signing_key:
65
+ specification_version: 3
66
+ summary: A sibling of ostruct that handles deeply nested structures and basic data
67
+ type detection
68
+ test_files:
69
+ - spec/deepstruct_spec.rb
70
+ - spec/delegation_spec.rb
71
+ - spec/spec_helper.rb