deepstruct 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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