mighty_struct 0.1.2 → 0.1.4
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.
- 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
|
+
[](https://travis-ci.org/msievers/mighty_struct)
|
4
|
+
[](https://codeclimate.com/github/msievers/mighty_struct/coverage)
|
5
|
+
[](https://codeclimate.com/github/msievers/mighty_struct)
|
6
|
+
[](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
|