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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e1befffda2350b4119620bc5a265ec2e7dfa61eb
4
- data.tar.gz: 1f9d05937b898767975ce10a400e4f16f69c7a73
3
+ metadata.gz: f8d7b9936e89b506c81ae023b4f503b838670c6a
4
+ data.tar.gz: d377473551801d74b6aeafa3a2b008c77690c756
5
5
  SHA512:
6
- metadata.gz: 809f0eb22a097d19788102ae76d90289c88d8faa76c3a9cefdfae60142aae3aff6c9f4118789ee7f45738af75a09a32a1e0356ea91789bff95a8d2030107985f
7
- data.tar.gz: 81da6ad10ef7d667c8b66c1f6c99a44f3679c1a7371e6ee3ad600bf941a4b38848a049d133242708fd4c249ee672d68fb6a2fa56af70ea3df78315387ca67360
6
+ metadata.gz: 894c82f523a88a02172d576649c41282c7620fde4e57a91b0011139c8d28e9ddd79ea7535b3a7c145fbaaa2f81f2ea853720471cca6a04e21c282e798a5960f2
7
+ data.tar.gz: 8e5fe24e3cb830edacc15af24e7dd2e8859cd1dc8a4c2171c6d4e44e187a2b73b6b71e39ebe0f36d203bf092b2622e925a47f5fa76645960c808cfef7141a2c7
data/.codeclimate.yml ADDED
@@ -0,0 +1,2 @@
1
+ exclude_paths:
2
+ - "benchmark/*"
data/.travis.yml CHANGED
@@ -1,5 +1,9 @@
1
1
  language: ruby
2
2
  rvm:
3
- - "2.0"
4
- - "2.1"
5
- - "2.2"
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
6
+ - 2.2.0
7
+ - jruby-1.7
8
+ - jruby-9.0.0.0.pre2
9
+ - rbx-2.5
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
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/mighty_struct`. To experiment with that code, run `bin/console` for an interactive prompt.
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
- TODO: Delete this and the text above, and describe your gem
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
- ## Installation
10
+ ## Key features
8
11
 
9
- Add this line to your application's Gemfile:
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
- gem 'mighty_struct'
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
- And then execute:
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
- $ bundle
61
+ But neither of them provided everything I wanted.
18
62
 
19
- Or install it yourself as:
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
- $ gem install mighty_struct
71
+ ## Why are real methods as property accessors cool?
22
72
 
23
- ## Usage
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
- TODO: Write usage instructions here
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/[my-github-username]/mighty_struct/fork )
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/joffrey/mab_document"
10
- require_relative "./benchmark/joffrey/mab_xml_parser"
9
+ require_relative "./benchmark/mighty_struct/mighty_struct_versus_others"
11
10
 
12
- #Benchmark::Joffrey::MabDocument.new.call
13
- Benchmark::Joffrey::MabXmlParser.new.call
11
+ Benchmark::MightyStruct::MightyStructVersusOthers.new.call
14
12
  end
@@ -0,0 +1,4 @@
1
+ require "benchmark/ips"
2
+
3
+ class Benchmark::MightyStruct
4
+ 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
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
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.define_property_accessors!(mighty_struct, object)
5
- if object.is_a?(Hash)
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
- def self.new?(object)
18
- object.is_a?(Array) || object.is_a?(Hash)
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
- self.class.define_property_accessors!(self, @object = object)
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
@@ -1,3 +1,3 @@
1
1
  class MightyStruct
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.4"
3
3
  end
@@ -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.2
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-20 00:00:00.000000000 Z
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.6
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