hashstructor 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1c5ba11f6bea507eada8ffef440f9c0ab35335ba
4
+ data.tar.gz: cfa2de7abfece15ca53d8814efc5a8d5699b9934
5
+ SHA512:
6
+ metadata.gz: c9d4e04bfe0737b868d16c8cc99250f10a195c8492f5b652a86e731b0c79e0efe62280c0715a80925b7b6e79bf37c68ffe4a03b3c102bbf4e0d92ed1932f5109
7
+ data.tar.gz: 7b50b74f329a59862be978b0657e1aa4bd4afa6cbbb0673ce01b11f07e354dc29bd9e4820dc4a6c7c70702d436a131f4b268e94fe87d7f869c975e9b754c4514
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.1
4
+ before_install: gem install bundler -v 1.10.5
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --private --list-undoc --markup markdown - LICENSE.md
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hashstructor.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Ed Ropple
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # Hashstructor
2
+
3
+ There comes a time in every wee programmer's life (and all programmers are, for the purposes of this readme, wee or previously wee) when one must take a big blob of JSON or YAML and make it into an object. Or worse, a hierarchy of objects. This leads to lots of very manual parsing and gnashing of teeth. Some libraries, like the mildly spiffy [constructor](https://github.com/atomicobject/constructor), allow you to defray some of this pain by letting you pass in hashes to build objects, but this only goes so far: no type coercion, no nested types.
4
+
5
+ Finally I got tired of this mess when building a game prototype and writing enough stupid read-in code of data files that I decided to Fix This Situation. (You can argue that I got sidetracked; I won't disagree.) After about three hours of late-night work, I'm somewhat proud to unveil **Hashstructor**: your one-stop shop for mangling the heck out of Ruby hashes and building neat-o object trees out of them. (The reverse, breaking these data structures down into hashes, is a TODO.)
6
+
7
+ Hashstructor is _fully documented_ (100% coverage in `yard`) and comes with a fairly exhaustive set of tests to prove that it actually does what it's supposed to do.
8
+
9
+ ## Installation ##
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'hashstructor'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install hashstructor
24
+
25
+ ## Usage ##
26
+
27
+ Hashstructor is pretty easy to deal with, but it's 2:10AM as I write this and I am very tired, so I shall direct you to the [fancy and/or schmancy](https://github.com/eropple/hashstructor/tree/master/spec) `spec` folder. In `test_classes` you will find examples of nearly every aspect of Hashstructor, and `hashstructor_spec.rb` explains them with a little context.
28
+
29
+ YARD documentation is hosted at http://eropple.github.io/hashstructor .
30
+
31
+ Hashstructor is designed for Ruby 2.0 and above, but _may_ run on Ruby 1.9.x; failure to do so is not a bug.
32
+
33
+ ## Development ##
34
+
35
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
36
+
37
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
38
+
39
+ ## Contributing ##
40
+
41
+ Bug reports and pull requests are welcome on GitHub at https://github.com/eropple/hashstructor. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
42
+
43
+
44
+ ## License ##
45
+
46
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
47
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "hashstructor"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
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
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'hashstructor/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "hashstructor"
8
+ spec.version = Hashstructor::VERSION
9
+ spec.authors = ["Ed Ropple"]
10
+ spec.email = ["ed@edropple.com"]
11
+
12
+ spec.summary = %q{A Ruby DSL and parser for converting hashes into trees of data objects.}
13
+ spec.homepage = "https://github.com/eropple/hashstructor"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.10"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "pry"
24
+ spec.add_development_dependency "rspec"
25
+ spec.add_development_dependency "yard"
26
+ end
@@ -0,0 +1,22 @@
1
+ require 'hashstructor/member'
2
+
3
+ module Hashstructor
4
+ # Class methods for {Hashstructor} objects.
5
+ module ClassMethods
6
+ # @return [Array<Member>] all Hashstructor members for this class (frozen)
7
+ def hashstructor_members
8
+ @hashstructor_members.freeze
9
+ end
10
+
11
+ private
12
+ # Defines a Hashstructor member to be placed on this object. See
13
+ # {Member}'s documentation for expected values.
14
+ def member(name, options = {})
15
+ options[:member_type] ||= :normal
16
+
17
+ options[:required] = !!(options[:required])
18
+
19
+ @hashstructor_members << Hashstructor::Member.new(self, name, options).freeze
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,70 @@
1
+ require 'set'
2
+
3
+ module Hashstructor
4
+ # Instance methods for {Hashstructor} objects.
5
+ module InstanceMethods
6
+ private
7
+ # Initializes the object. This exists for objects that `prepend`
8
+ # {Hashstructor}; objects that `include` it must explicitly invoke
9
+ # {#hashstruct} within their own `initialize` method.
10
+ #
11
+ # @param hash [Hash] the hash from which to parse the object data
12
+ def initialize(hash)
13
+ hashstruct(hash)
14
+ end
15
+
16
+ # Constructs the object from the given hash, based on the metadata fed
17
+ # in by {ClassMethods#member} invocations.
18
+ #
19
+ # @param hash [Hash] the hash from which to parse the object data
20
+ def hashstruct(hash)
21
+ raise HashstructorError, "hashstruct expects a hash." unless hash.is_a?(Hash)
22
+
23
+ members = self.class.hashstructor_members
24
+
25
+ # This could be done inline, but I'd rather loop twice and get this check
26
+ # out of the way ahead of time because of the perf hit when dealing with
27
+ # deeply nested structures.
28
+ partitioned_members = members.partition { |m| hash[m.name] || hash[m.name.to_s] }
29
+
30
+ existing_members = partitioned_members[0]
31
+ missing_members = partitioned_members[1]
32
+ raise HashstructorError, "Missing required members: #{missing_members.map { |m| m.name }.join(", ")}" \
33
+ if missing_members.any? { |m| m.required }
34
+
35
+ existing_members.each do |member|
36
+ raw_value = hash[member.name] || hash[member.name.to_s]
37
+ out_value = nil
38
+
39
+ case member.member_type
40
+ when :normal
41
+ raise HashstructorError, "member '#{member.name}' expects a single value, got #{raw_value.class.name}." \
42
+ if member.options[:no_collections] && raw_value.is_a?(Enumerable)
43
+
44
+ out_value = member.parse_single(raw_value)
45
+
46
+ when :hash
47
+ raise HashstructorError, "member '#{member.name}' expects a Hash, got #{raw_value.class.name}." \
48
+ unless raw_value.is_a?(Hash)
49
+
50
+ out_value = {}
51
+ raw_value.each_pair do |k, v|
52
+ key = member.options[:string_keys] ? k.to_s : k.to_s.to_sym
53
+
54
+ out_value[key] = member.parse_single(v)
55
+ end
56
+
57
+ when :array, :set
58
+ raise HashstructorError, "member '#{member.name}' expects an Enumerable, got #{raw_value.class.name}"\
59
+ unless raw_value.is_a?(Enumerable)
60
+
61
+ out_value = raw_value.map { |v| member.parse_single(v) }
62
+
63
+ out_value = out_value.to_set if member.member_type == :set
64
+ end
65
+
66
+ instance_variable_set("@#{member.name}", out_value)
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,138 @@
1
+ module Hashstructor
2
+ # A member within a Hashstructor class that should be populated by the hash
3
+ # passed into {Hashstructor#initialize}.
4
+ class Member
5
+ # The allowed {#member_type}s for a `Member`:
6
+ #
7
+ # - `:normal` - a normal value. raises error if hash or array given unless
8
+ # the `:no_collections` option is set.
9
+ # - `:hash` - a hash, with all keys symbolized unless the `:string_keys`
10
+ # option is set.
11
+ # - `:array` - an array
12
+ # - `:set` - a set
13
+ #
14
+ # Note that these types refer to the "collection status" of the member. A
15
+ # `:normal` member will happily accept any value unless a `:custom_type`
16
+ # option is passed.
17
+ VALID_MEMBER_TYPES = [ :normal, :hash, :set, :array ].freeze
18
+
19
+ # @return [Symbol] the name of the member, in the parsed hash.
20
+ attr_reader :name
21
+
22
+ # @return [Symbol] the type of the member; see {VALID_MEMBER_TYPES}.
23
+ attr_reader :member_type
24
+
25
+ # A hash of Class => Proc converters for value types. Any value type
26
+ # in this list, as well as any class that includes or prepends
27
+ # `Hashstructor`, is valid for {#value_type}. This is _intentionally_
28
+ # not frozen; if you want to extend it for your own use cases, I'm not
29
+ # going to get in your way.
30
+ VALID_VALUE_TYPES = {
31
+ String => Proc.new do |v|
32
+ v.to_s
33
+ end,
34
+ Symbol => Proc.new do |v|
35
+ v.to_sym
36
+ end,
37
+ Integer => Proc.new do |v|
38
+ Integer(v.to_s)
39
+ end,
40
+ Float => Proc.new do |v|
41
+ Float(v.to_s)
42
+ end,
43
+ TrueClass => Proc.new do |v|
44
+ !!v
45
+ end,
46
+ FalseClass => Proc.new do |v|
47
+ !!v
48
+ end,
49
+ }
50
+ # Determines the class that Hashstructor should attempt to coerce a
51
+ # given value into. For example, `Fixnum` will attempt to coerce a
52
+ # scalar value into a `Fixnum`.
53
+ #
54
+ # The valid types for {#value_type} are:
55
+ #
56
+ # - `String`
57
+ # - `Symbol`
58
+ # - `Integer`
59
+ # - `Float`
60
+ # - `TrueClass` or `FalseClass` (for booleans)
61
+ # - any class that includes or prepends `Hashstructor`
62
+ #
63
+ # @return [(nil, Class)] the type of the value stored in this member.
64
+ attr_reader :value_type
65
+
66
+ @required = false
67
+ # If true, attempting to construct this object without setting this
68
+ # member will raise an error.
69
+ attr_reader :required
70
+
71
+ # Specifies what type of attr (nil, `:reader`, or `:accessor`) that this
72
+ # member should define on its class.
73
+ #
74
+ # @return [(nil, :reader, :accessor)] the type of attr to create.
75
+ attr_reader :attr_kind
76
+
77
+ # @return [Hash] a (frozen) set of all extra options passed to the member.
78
+ attr_reader :options
79
+
80
+ def initialize(klass, name, options)
81
+ member_type = options[:member_type] || :normal
82
+ options.delete(:member_type)
83
+ value_type = options[:value_type]
84
+ options.delete(:value_type)
85
+ required = !!(options[:required])
86
+ options.delete(:required)
87
+ attr_kind = options[:attr_kind]
88
+ options.delete(:attr_kind)
89
+
90
+ @options = options.freeze
91
+
92
+ raise HashstructorError, "'name' must respond to :to_sym." unless name.respond_to?(:to_sym)
93
+ @name = name.to_sym
94
+
95
+ raise HashstructorError, "'member_type' (#{member_type}) must be one of: [ #{VALID_MEMBER_TYPES.join(", ")} ]" \
96
+ unless VALID_MEMBER_TYPES.include?(member_type.to_sym)
97
+
98
+ @member_type = member_type.to_sym
99
+
100
+ raise HashstructorError, "'value_type' must be nil or a Class." \
101
+ unless value_type == nil || value_type.is_a?(Class)
102
+ raise HashstructorError, "'value_type' class, #{value_type.name}, must be in " +
103
+ "VALID_VALUE_TYPES ([ #{VALID_VALUE_TYPES.keys.map { |c| c.name}.join(", ")} ]) " +
104
+ "or include or prepend Hashstructor." \
105
+ unless value_type == nil || VALID_VALUE_TYPES[value_type] != nil || value_type.ancestors.include?(Hashstructor)
106
+
107
+ @value_type = value_type
108
+
109
+ @required = required
110
+
111
+ case attr_kind
112
+ when nil
113
+ # do nothing
114
+ when :reader
115
+ klass.send(:attr_reader, @name)
116
+ when :accessor
117
+ klass.send(:attr_accessor, @name)
118
+ else
119
+ raise HashstructorError, "Unrecognized attr_kind: #{attr_kind}"
120
+ end
121
+ @attr_kind = attr_kind
122
+ end
123
+
124
+ # Parses the passed-in value (always a single item; {InstanceMethods#hashstruct} handles
125
+ # the breaking down of arrays and hashes) and returns a value according to {#value_type}.
126
+ def parse_single(value)
127
+ if value_type.nil?
128
+ value
129
+ elsif value_type.ancestors.include?(Hashstructor)
130
+ raise HashstructorError, "No hash provided for building a Hashstructor object." unless value.is_a?(Hash)
131
+
132
+ value_type.new(value)
133
+ else
134
+ VALID_VALUE_TYPES[value_type].call(value)
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,4 @@
1
+ module Hashstructor
2
+ # The gem version. As one does.
3
+ VERSION = "1.0.0"
4
+ end
@@ -0,0 +1,40 @@
1
+ require "hashstructor/version"
2
+
3
+ module Hashstructor
4
+ # You made Hashstructor sad and now you have this error.
5
+ class HashstructorError < RuntimeError
6
+ end
7
+ end
8
+
9
+ require "hashstructor/instance_methods"
10
+ require "hashstructor/class_methods"
11
+
12
+ # Allows the "hashstruction" of objects by predefining an {Hashstructor#initialize}
13
+ # method off of the {Hashstructor::InstanceMethods#member} declarations within the
14
+ # class spec.
15
+ #
16
+ # The secret sauce for this module is in {Hashstructor::InstanceMethods} and
17
+ # {Hashstructor::ClassMethods}, respectively.
18
+ module Hashstructor
19
+ prepend Hashstructor::InstanceMethods
20
+
21
+ # Fires when this module is `prepend`ed. This is the most cromulent way to
22
+ # use Hashstructor in Ruby 2.x.
23
+ def self.prepended(mod)
24
+ Hashstructor.decorate(mod)
25
+ end
26
+
27
+ # Fires when this module is `include`d. This is provided for Ruby 1.9.x, but
28
+ # classes that `include` this have to explicitly invoke
29
+ # {Hashstructor::InstanceMethods#hashstruct}.
30
+ def self.included(mod)
31
+ Hashstructor.decorate(mod)
32
+ end
33
+
34
+ # The actual magic of both {Hashstructor#prepended} and {Hashstructor#included};
35
+ # bolts the class methods onto the prepending/including module.
36
+ def self.decorate(mod)
37
+ mod.instance_variable_set("@hashstructor_members", [])
38
+ mod.extend(Hashstructor::ClassMethods)
39
+ end
40
+ end
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hashstructor
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Ed Ropple
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-07-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
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'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: yard
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description:
84
+ email:
85
+ - ed@edropple.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".rspec"
92
+ - ".travis.yml"
93
+ - ".yardopts"
94
+ - CODE_OF_CONDUCT.md
95
+ - Gemfile
96
+ - LICENSE.txt
97
+ - README.md
98
+ - Rakefile
99
+ - bin/console
100
+ - bin/setup
101
+ - hashstructor.gemspec
102
+ - lib/hashstructor.rb
103
+ - lib/hashstructor/class_methods.rb
104
+ - lib/hashstructor/instance_methods.rb
105
+ - lib/hashstructor/member.rb
106
+ - lib/hashstructor/version.rb
107
+ homepage: https://github.com/eropple/hashstructor
108
+ licenses:
109
+ - MIT
110
+ metadata: {}
111
+ post_install_message:
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ requirements: []
126
+ rubyforge_project:
127
+ rubygems_version: 2.4.6
128
+ signing_key:
129
+ specification_version: 4
130
+ summary: A Ruby DSL and parser for converting hashes into trees of data objects.
131
+ test_files: []
132
+ has_rdoc: