mighty_struct 0.1.2 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +2 -0
- data/.travis.yml +7 -3
- data/Gemfile +1 -2
- data/README.md +62 -12
- data/Rakefile +2 -4
- data/benchmark/mighty_struct.rb +4 -0
- data/benchmark/mighty_struct/mighty_struct_versus_others.rb +60 -0
- data/bin/console +7 -6
- data/lib/mighty_struct.rb +23 -15
- data/lib/mighty_struct/version.rb +1 -1
- data/mighty_struct.gemspec +1 -0
- metadata +20 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f8d7b9936e89b506c81ae023b4f503b838670c6a
|
4
|
+
data.tar.gz: d377473551801d74b6aeafa3a2b008c77690c756
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 894c82f523a88a02172d576649c41282c7620fde4e57a91b0011139c8d28e9ddd79ea7535b3a7c145fbaaa2f81f2ea853720471cca6a04e21c282e798a5960f2
|
7
|
+
data.tar.gz: 8e5fe24e3cb830edacc15af24e7dd2e8859cd1dc8a4c2171c6d4e44e187a2b73b6b71e39ebe0f36d203bf092b2622e925a47f5fa76645960c808cfef7141a2c7
|
data/.codeclimate.yml
ADDED
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -3,9 +3,8 @@ source "https://rubygems.org"
|
|
3
3
|
# Specify your gem's dependencies in your gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
-
if !ENV["CI"]
|
6
|
+
if !ENV["CI"] && RUBY_ENGINE == "ruby"
|
7
7
|
group :development do
|
8
|
-
gem "hashdiff"
|
9
8
|
gem "pry", "~> 0.9.12.6"
|
10
9
|
gem "pry-byebug", "<= 1.3.2"
|
11
10
|
gem "pry-rescue", "~> 1.4.1", github: "ConradIrwin/pry-rescue", branch: :master
|
data/README.md
CHANGED
@@ -1,28 +1,78 @@
|
|
1
1
|
# MightyStruct
|
2
2
|
|
3
|
-
|
3
|
+
[![Build Status](https://travis-ci.org/msievers/mighty_struct.svg)](https://travis-ci.org/msievers/mighty_struct)
|
4
|
+
[![Test Coverage](https://codeclimate.com/github/msievers/mighty_struct/badges/coverage.svg)](https://codeclimate.com/github/msievers/mighty_struct/coverage)
|
5
|
+
[![Code Climate](https://codeclimate.com/github/msievers/mighty_struct/badges/gpa.svg)](https://codeclimate.com/github/msievers/mighty_struct)
|
6
|
+
[![Dependency Status](https://gemnasium.com/msievers/mighty_struct.svg)](https://gemnasium.com/msievers/mighty_struct)
|
4
7
|
|
5
|
-
|
8
|
+
`MightyStruct` is an object wrapper which gives deep method access to properties. It combines beneficial features from functionally related projects like `OpenStruct` and `Hashie::Mash` into an non-inversive, transparant decorator like object wrapper.
|
6
9
|
|
7
|
-
##
|
10
|
+
## Key features
|
8
11
|
|
9
|
-
|
12
|
+
* wraps any object that is an `Enumerable` (e.g `Array` or `Hash`)
|
13
|
+
* creates method accessors for any object that additionally responds to `:keys` (e.g. `Hash`)
|
14
|
+
* deep method access to object properties
|
15
|
+
* property accessors are implemented via methods, not `method_missing`
|
16
|
+
* as a result tab completion in pry works
|
17
|
+
* dispite property accessors, the namespace of wrapped objects isn't touched
|
18
|
+
* all method calls which don't hit a property accessor are dispatched to the wrapped object
|
19
|
+
* results are again wrapped to instances of MightyStruct if possible
|
20
|
+
* the wrapped object can be retrieved at any time using `MightyStruct.to_object(obj)`
|
21
|
+
|
22
|
+
## Example
|
10
23
|
|
11
24
|
```ruby
|
12
|
-
|
25
|
+
require "mighty_struct"
|
26
|
+
|
27
|
+
hash = {
|
28
|
+
a: [
|
29
|
+
{ b: 1 },
|
30
|
+
{ b: 2 },
|
31
|
+
],
|
32
|
+
}
|
33
|
+
|
34
|
+
# create it from some hash or array
|
35
|
+
mighty_struct = MightyStruct.new(hash)
|
36
|
+
|
37
|
+
# access deeply nested properties
|
38
|
+
mighty_struct.a[0].b # => 1
|
39
|
+
|
40
|
+
# call methods transparently on the wrapped objects
|
41
|
+
mighty_struct.a.last.b # => 2
|
42
|
+
|
43
|
+
# get back the original object ... look ma', it's still the same hash
|
44
|
+
MightyStruct.to_object(mighty_struct).eql?(hash) # => true
|
13
45
|
```
|
14
46
|
|
15
|
-
|
47
|
+
Or play with it on your own. It's just one command (line) away.
|
48
|
+
|
49
|
+
```bash
|
50
|
+
git clone https://github.com/msievers/mighty_struct.git && cd mighty_struct && bundle && bin/console
|
51
|
+
```
|
52
|
+
|
53
|
+
## Another of this "method invocation" hashes? Really?!
|
54
|
+
|
55
|
+
Before I started coding this, I tried the following three alternatives
|
56
|
+
|
57
|
+
* `OpenStruct`
|
58
|
+
* `recursive-open-struct`
|
59
|
+
* `Hashie::Mash`
|
16
60
|
|
17
|
-
|
61
|
+
But neither of them provided everything I wanted.
|
18
62
|
|
19
|
-
|
63
|
+
| MightyStruct | OpenStruct | recursive-open-struct | Hashie::Mash
|
64
|
+
--- | :----------: | :--------: | :-------------------: | :----------:
|
65
|
+
deep method access | :heavy_check_mark: | :heavy_multiplication_x: | (:heavy_check_mark:) | :heavy_check_mark:
|
66
|
+
real method accessors | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_multiplication_x:
|
67
|
+
works without object dupping | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_multiplication_x: | :heavy_multiplication_x:
|
68
|
+
transparent method dispatching | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_multiplication_x: | :heavy_multiplication_x:
|
69
|
+
original object retrieval | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_multiplication_x: | :heavy_multiplication_x:
|
20
70
|
|
21
|
-
|
71
|
+
## Why are real methods as property accessors cool?
|
22
72
|
|
23
|
-
|
73
|
+
Method accessors for object properties can either be implemented via `method_missing` or by defining (singleton) methods. The benefit of real methods is, that if you are using a debugger (e.g. `pry`), you can use tab completion to discover methods defined on a object. This does not work for `method_missing` based accessors.
|
24
74
|
|
25
|
-
|
75
|
+
With real method accessors in place, playing with a `mighty_struct` within `pry` just feels like working within a shell.
|
26
76
|
|
27
77
|
## Development
|
28
78
|
|
@@ -32,7 +82,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
32
82
|
|
33
83
|
## Contributing
|
34
84
|
|
35
|
-
1. Fork it ( https://github.com/
|
85
|
+
1. Fork it ( https://github.com/msievers/mighty_struct/fork )
|
36
86
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
37
87
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
38
88
|
4. Push to the branch (`git push origin my-new-feature`)
|
data/Rakefile
CHANGED
@@ -6,9 +6,7 @@ RSpec::Core::RakeTask.new(:spec)
|
|
6
6
|
task :default => :spec
|
7
7
|
|
8
8
|
task :benchmark do
|
9
|
-
require_relative "./benchmark/
|
10
|
-
require_relative "./benchmark/joffrey/mab_xml_parser"
|
9
|
+
require_relative "./benchmark/mighty_struct/mighty_struct_versus_others"
|
11
10
|
|
12
|
-
|
13
|
-
Benchmark::Joffrey::MabXmlParser.new.call
|
11
|
+
Benchmark::MightyStruct::MightyStructVersusOthers.new.call
|
14
12
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require "hashie/mash"
|
2
|
+
require "mighty_struct"
|
3
|
+
require "pry"
|
4
|
+
require_relative "../mighty_struct"
|
5
|
+
|
6
|
+
class Benchmark::MightyStruct::MightyStructVersusOthers
|
7
|
+
def call
|
8
|
+
hash = {
|
9
|
+
a: 1,
|
10
|
+
b: {
|
11
|
+
c: 2,
|
12
|
+
"d" => 3,
|
13
|
+
e: [
|
14
|
+
{
|
15
|
+
f: 4
|
16
|
+
}
|
17
|
+
]
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
puts "\n"
|
22
|
+
|
23
|
+
Benchmark.ips do |x|
|
24
|
+
puts "Hashie::Mash.new(hash)"
|
25
|
+
puts "MightyStruct.new(hash)"
|
26
|
+
puts "OpenStruct.new(hash)"
|
27
|
+
puts "\n"
|
28
|
+
|
29
|
+
x.report("Hashie::Mash") { Hashie::Mash.new(hash) }
|
30
|
+
x.report("MightyStruct") { MightyStruct.new(hash) }
|
31
|
+
x.report("OpenStruct") { OpenStruct.new(hash) }
|
32
|
+
|
33
|
+
x.compare!
|
34
|
+
end
|
35
|
+
|
36
|
+
[:enabled, :disabled].each do |_caching_mode|
|
37
|
+
puts "Hashie::Mash.new(hash).b.c"
|
38
|
+
puts "MightyStruct.new(hash, caching: :#{_caching_mode}).b.c"
|
39
|
+
puts "OpenStruct.new(hash).b.c"
|
40
|
+
puts "\n"
|
41
|
+
|
42
|
+
Benchmark.ips do |x|
|
43
|
+
hashie_mash = Hashie::Mash.new(hash)
|
44
|
+
mighty_struct = MightyStruct.new(hash, caching: _caching_mode)
|
45
|
+
open_struct = OpenStruct.new(hash)
|
46
|
+
open_struct.b = OpenStruct.new(open_struct.b)
|
47
|
+
|
48
|
+
if hashie_mash.b.c != hash[:b][:c] || mighty_struct.b.c != hash[:b][:c] || open_struct.b.c != hash[:b][:c]
|
49
|
+
raise
|
50
|
+
end
|
51
|
+
|
52
|
+
x.report("Hashie::Mash") { hashie_mash.b.c }
|
53
|
+
x.report("MightyStruct") { mighty_struct.b.c }
|
54
|
+
x.report("OpenStruct") { open_struct.b.c }
|
55
|
+
|
56
|
+
x.compare!
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/bin/console
CHANGED
@@ -6,9 +6,10 @@ require "mighty_struct"
|
|
6
6
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
7
|
# with your gem easier. You can also use a different console, if you like.
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
require "irb"
|
14
|
-
IRB.start
|
9
|
+
begin
|
10
|
+
require "pry"
|
11
|
+
Pry.start
|
12
|
+
rescue LoadError
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
15
|
+
end
|
data/lib/mighty_struct.rb
CHANGED
@@ -1,29 +1,36 @@
|
|
1
1
|
require "mighty_struct/version"
|
2
2
|
|
3
3
|
class MightyStruct
|
4
|
-
def self.
|
5
|
-
|
6
|
-
object.keys.each do |_key|
|
7
|
-
class_eval <<-EORUBY, __FILE__, __LINE__ + 1
|
8
|
-
def #{_key}
|
9
|
-
value = @object[#{_key.is_a?(Symbol) ? ':' << _key.to_s : '"' << _key << '"'}]
|
10
|
-
self.class.new?(value) ? self.class.new(value) : value
|
11
|
-
end
|
12
|
-
EORUBY
|
13
|
-
end
|
14
|
-
end
|
4
|
+
def self.new?(object)
|
5
|
+
object.is_a?(Enumerable)
|
15
6
|
end
|
16
7
|
|
17
|
-
|
18
|
-
|
8
|
+
# in order not to pollute the instance's method namespace this is a class method
|
9
|
+
def self.to_object(object)
|
10
|
+
object.is_a?(self) ? object.instance_variable_get(:@object) : object
|
19
11
|
end
|
20
12
|
|
21
|
-
def initialize(object)
|
13
|
+
def initialize(object, options = {})
|
22
14
|
unless self.class.new?(object)
|
23
15
|
raise ArgumentError.new("Cannot create a an instance of #{self.class} for the given object!")
|
24
16
|
end
|
25
17
|
|
26
|
-
|
18
|
+
@cache = {}
|
19
|
+
@cache_mode = options[:caching] || :enabled
|
20
|
+
|
21
|
+
if (@object = object).respond_to?(:keys)
|
22
|
+
object.keys.each do |_key|
|
23
|
+
unless respond_to?(_key)
|
24
|
+
define_singleton_method(_key) do
|
25
|
+
if @cache_mode == :disabled
|
26
|
+
self.class.new?(value = @object[_key]) ? self.class.new(value) : value
|
27
|
+
else
|
28
|
+
@cache[_key] ||= self.class.new?(value = @object[_key]) ? self.class.new(value) : value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
27
34
|
end
|
28
35
|
|
29
36
|
#
|
@@ -31,6 +38,7 @@ class MightyStruct
|
|
31
38
|
#
|
32
39
|
def method_missing(method_name, *arguments, &block)
|
33
40
|
if @object.respond_to?(method_name)
|
41
|
+
@cache.clear if @cache_mode == :smart # clear the properties cache if we are smart
|
34
42
|
result = @object.send(method_name, *arguments, &block)
|
35
43
|
|
36
44
|
# ensure that results of called methods are mighty structs again
|
data/mighty_struct.gemspec
CHANGED
@@ -17,6 +17,7 @@ Gem::Specification.new do |spec|
|
|
17
17
|
|
18
18
|
spec.add_development_dependency "benchmark-ips"
|
19
19
|
spec.add_development_dependency "bundler", ">= 1.3"
|
20
|
+
spec.add_development_dependency "hashie"
|
20
21
|
spec.add_development_dependency "rake"
|
21
22
|
spec.add_development_dependency "rspec", ">= 3.0.0", "< 4.0.0"
|
22
23
|
spec.add_development_dependency "simplecov", ">= 0.8.0"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mighty_struct
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Sievers
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-05-
|
11
|
+
date: 2015-05-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: benchmark-ips
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: hashie
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rake
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -92,12 +106,15 @@ executables: []
|
|
92
106
|
extensions: []
|
93
107
|
extra_rdoc_files: []
|
94
108
|
files:
|
109
|
+
- ".codeclimate.yml"
|
95
110
|
- ".gitignore"
|
96
111
|
- ".rspec"
|
97
112
|
- ".travis.yml"
|
98
113
|
- Gemfile
|
99
114
|
- README.md
|
100
115
|
- Rakefile
|
116
|
+
- benchmark/mighty_struct.rb
|
117
|
+
- benchmark/mighty_struct/mighty_struct_versus_others.rb
|
101
118
|
- bin/console
|
102
119
|
- bin/setup
|
103
120
|
- lib/mighty_struct.rb
|
@@ -122,7 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
139
|
version: '0'
|
123
140
|
requirements: []
|
124
141
|
rubyforge_project:
|
125
|
-
rubygems_version: 2.4.
|
142
|
+
rubygems_version: 2.4.7
|
126
143
|
signing_key:
|
127
144
|
specification_version: 4
|
128
145
|
summary: A mighty struct which combines OpenStruct like method access with reasonable
|