destructure 0.0.12

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5d1c33debe9be83eab196be5c71b8602b4467a0e
4
+ data.tar.gz: d28c21e0de1f1ddd88bbfa73a858025375f13fba
5
+ SHA512:
6
+ metadata.gz: d4266394d4d8ee34872c810c777ca1882a43f6cc1d769f62855ef8e5eb16e38b4a3cf10c09ee1a0c7418932e6faba1a2eb6fa226b08a3ea4952b3217158e38bb
7
+ data.tar.gz: 52ede93459670e44d1407263210267aedae88a6a69f184fb271e24ef709b5148675f251644e34ccc02b00bd94b2cd7584ccb12eaa3eb63773fd1f44aa5f8f802
@@ -0,0 +1 @@
1
+ require_relative './destructure/destructure'
@@ -0,0 +1,90 @@
1
+ require 'sourcify'
2
+ require 'active_support/inflector'
3
+ require 'paramix'
4
+ require 'binding_of_caller'
5
+ require 'destructure/dmatch'
6
+ require 'destructure/sexp_transformer'
7
+
8
+ module Destructure
9
+
10
+ include Paramix::Parametric
11
+
12
+ parameterized do |params|
13
+
14
+ private :bind_locals do
15
+ bind = params[:bind_locals]
16
+ @bind_locals ||= bind.nil? ? true : bind
17
+ end
18
+
19
+ if params[:env_name]
20
+ private params[:env_name] do
21
+ @_my_destructure_env
22
+ end
23
+ private :set_custom_env do |value|
24
+ @_my_destructure_env = value
25
+ end
26
+ end
27
+
28
+ if params[:matcher_name]
29
+ private params[:matcher_name] do |&pattern|
30
+ proc { |x| dbind(x, &pattern) }
31
+ end
32
+ end
33
+ end
34
+
35
+ def dbind(x, &pat_block)
36
+ dbind_internal(x, pat_block.to_sexp(strip_enclosure: true, ignore_nested: true), binding.of_caller(1), caller_locations(1,1)[0].label)
37
+ end
38
+
39
+ private ########################################
40
+
41
+ def bind_locals
42
+ true
43
+ end
44
+
45
+ def dbind_internal(x, sexp, caller_binding, caller_location)
46
+ env = dbind_no_ostruct_sexp(x, sexp, caller_binding)
47
+ return nil if env.nil?
48
+
49
+ if bind_locals
50
+ env.keys.each {|k| _destructure_set(k.name, env[k], caller_binding, caller_location)}
51
+ end
52
+
53
+ ostruct_env = env.to_openstruct
54
+ set_custom_env(ostruct_env) if self.respond_to?(:set_custom_env, true)
55
+ ostruct_env
56
+ end
57
+
58
+ def dbind_no_ostruct_sexp(x, sexp, caller_binding)
59
+ sp = sexp
60
+ pat = SexpTransformer.transform(sp, caller_binding)
61
+ DMatch::match(pat, x)
62
+ end
63
+
64
+ def _destructure_set(name, value, binding, caller)
65
+ if name.is_a?(String) || binding.eval("defined? #{name}") == 'local-variable'
66
+ $binding_temp = value
67
+ binding.eval("#{name} = $binding_temp")
68
+ else
69
+ if binding.eval('self').respond_to?(name, true)
70
+ raise "Cannot have pattern variable named '#{name}'. A method already exists with that name. Choose a different name, " +
71
+ "or pre-initialize a local variable that shadows the method."
72
+ end
73
+ @_destructure_env ||= {}
74
+ @_destructure_env[caller] ||= {}
75
+ @_destructure_env[caller][name] = value
76
+ end
77
+ end
78
+
79
+ def method_missing(name, *args, &block)
80
+ if bind_locals
81
+ c = caller_locations(1,1)[0].label
82
+ @_destructure_env ||= {}
83
+ caller_hash = @_destructure_env[c]
84
+ caller_hash && caller_hash.keys.include?(name) ? caller_hash[name] : super
85
+ else
86
+ super
87
+ end
88
+ end
89
+
90
+ end
@@ -0,0 +1,160 @@
1
+ require 'destructure/env'
2
+ require 'destructure/types'
3
+
4
+ class DMatch
5
+ def self.match(pat, x)
6
+ DMatch.new(Env.new).match(pat, x)
7
+ end
8
+
9
+ def self._
10
+ Wildcard.instance
11
+ end
12
+
13
+ def initialize(env)
14
+ @env = env
15
+ end
16
+
17
+ def match(pat, x)
18
+ case
19
+ when pat.is_a?(Wildcard); @env
20
+ when pat.is_a?(Pred) && pat.test(x, @env); @env
21
+ when pat.is_a?(FilterSplat); match_filter_splat(pat, x)
22
+ when pat.is_a?(SelectSplat); match_select_splat(pat, x)
23
+ when pat.is_a?(Splat); match_splat(pat, x)
24
+ when pat.is_a?(Var) && pat.test(x, @env); match_var(pat, x)
25
+ when pat.is_a?(Obj) && pat.test(x, @env) && all_field_patterns_match(pat, x); @env
26
+ when pat.is_a?(String) && pat == x; @env
27
+ when pat.is_a?(Regexp); match_regexp(pat, x)
28
+ when pat.is_a?(Or); match_or(pat, x)
29
+ when hash(pat, x) && all_keys_match(pat, x); @env
30
+ when enumerable(pat, x); match_enumerable(pat, x)
31
+ when pat == x; @env
32
+ else; nil
33
+ end
34
+ end
35
+
36
+ private ###########################################################
37
+
38
+ def all_keys_match(pat, x)
39
+ all_match(pat.keys.map { |k| x.keys.include?(k) && match(pat[k], x[k]) })
40
+ end
41
+
42
+ def match_regexp(pat, x)
43
+ m = pat.match(x)
44
+ m && @env.merge!(Hash[pat.named_captures.keys.map { |k| [Var.new(k.to_sym), m[k]] }])
45
+ end
46
+
47
+ def all_field_patterns_match(pat, x)
48
+ all_match(pat.fields.keys.map { |name| x.respond_to?(name) && match(pat.fields[name], x.send(name)) })
49
+ end
50
+
51
+ def match_var(pat, x)
52
+ @env.bind(pat, x)
53
+ end
54
+
55
+ def match_or(pat, x)
56
+ pat.patterns.lazy.map{|p| match(p, x)}.reject{|e| e.nil?}.first
57
+ end
58
+
59
+ def match_splat(pat, x)
60
+ @env.bind(pat, enumerable(x) ? x : [x])
61
+ end
62
+
63
+ def match_select_splat(pat, x)
64
+ x_match_and_env = x.map { |z| [z, DMatch::match(pat.pattern, z)] }.reject { |q| q.last.nil? }.first
65
+ if x_match_and_env
66
+ x_match, env = x_match_and_env
67
+ @env.bind(pat, x_match) && @env.merge!(env)
68
+ else
69
+ nil
70
+ end
71
+ end
72
+
73
+ def match_filter_splat(pat, x)
74
+ @env.bind(pat, x.map { |z| [z, match(pat.pattern, z)] }.reject { |q| q.last.nil? }.map { |q| q.first })
75
+ end
76
+
77
+ def match_enumerable(pat, x)
78
+ case
79
+ when (parts = decompose_splatted_enumerable(pat))
80
+ pat_before, pat_splat, pat_after = parts
81
+ x_before = x.take(pat_before.length)
82
+ if pat_after.any?
83
+ splat_len = len(x) - pat_before.length - pat_after.length
84
+ return nil if splat_len < 0
85
+ x_splat = x.drop(pat_before.length).take(splat_len)
86
+ else
87
+ x_splat = x.drop(pat_before.length)
88
+ end
89
+
90
+ before_and_splat_result = match_enumerable_no_splats(pat_before, x_before) && match(pat_splat, x_splat)
91
+
92
+ if before_and_splat_result && pat_after.any?
93
+ # do this only if we have to, since it requires access to the end of the enumerable,
94
+ # which doesn't work with infinite enumerables
95
+ x_after = take_last(pat_after.length, x)
96
+ match_enumerable_no_splats(pat_after, x_after)
97
+ else
98
+ before_and_splat_result
99
+ end
100
+ when len(pat) == len(x)
101
+ match_enumerable_no_splats(pat, x)
102
+ else; nil
103
+ end
104
+ end
105
+
106
+ def decompose_splatted_enumerable(pat)
107
+ before = []
108
+ splat = nil
109
+ after = []
110
+ pat.each do |p|
111
+ case
112
+ when p.is_a?(Splat)
113
+ if splat.nil?
114
+ splat = p
115
+ else
116
+ raise "cannot have more than one splat in a single array: #{pat.inspect}"
117
+ end
118
+ when splat.nil?
119
+ before.push(p)
120
+ else
121
+ after.push(p)
122
+ end
123
+ end
124
+
125
+ splat && [before, splat, after]
126
+ end
127
+
128
+ def take_last(n, xs)
129
+ result = []
130
+ xs.reverse_each do |x|
131
+ break if result.length == n
132
+ result.unshift x
133
+ end
134
+ result
135
+ end
136
+
137
+ def len(x)
138
+ x.respond_to?(:length) ? x.length : x.count
139
+ end
140
+
141
+ def match_enumerable_no_splats(pat, x)
142
+ all_match(pat.zip(x).map{|a| match(*a)}) ? @env : nil
143
+ end
144
+
145
+ def enumerable(*xs)
146
+ xs.all?{|x| x.is_a?(Enumerable)}
147
+ end
148
+
149
+ def hash(*xs)
150
+ xs.all?{|x| x.is_a?(Hash)}
151
+ end
152
+
153
+ def all_match(xs)
154
+ xs.all?{|x| x.is_a?(Env)}
155
+ end
156
+
157
+ class Wildcard
158
+ include Singleton
159
+ end
160
+ end
@@ -0,0 +1,49 @@
1
+ require 'ostruct'
2
+ require 'destructure/dmatch'
3
+ require 'destructure/types'
4
+
5
+ class DMatch
6
+ class Env
7
+
8
+ def env
9
+ @env ||= {}
10
+ end
11
+
12
+ def [](identifier)
13
+ raise 'identifier must be a Var or symbol' unless (identifier.is_a? Var) || (identifier.is_a? Symbol)
14
+ if identifier.is_a? Symbol
15
+ identifier = env.keys.select{|k| k.name == identifier}.first || identifier
16
+ end
17
+ v = env[identifier]
18
+ raise "Identifier '#{identifier}' is not bound." if v.nil?
19
+ v.is_a?(EnvNil) ? nil : v
20
+ end
21
+
22
+ def bind(identifier, value)
23
+ raise 'identifier must be a Var' unless identifier.is_a? Var
24
+ value_to_store = value.nil? ? EnvNil.new : value
25
+ existing_key = env.keys.select{|k| k == identifier || (k.name.is_a?(Symbol) && k.name == identifier.name)}.first
26
+ return nil if existing_key &&
27
+ (DMatch.match(env[existing_key], value_to_store).nil? ||
28
+ DMatch.match(value_to_store, env[existing_key]).nil?)
29
+ env[existing_key || identifier] = value_to_store
30
+ self
31
+ end
32
+
33
+ alias []= bind
34
+
35
+ def keys
36
+ env.keys
37
+ end
38
+
39
+ def to_openstruct
40
+ OpenStruct.new(Hash[env.map{|kv| [kv.first.name, kv.last]}])
41
+ end
42
+
43
+ def merge!(other_env)
44
+ other_env.keys.any?{|k| bind(k, other_env[k]).nil?} ? nil : self
45
+ end
46
+
47
+ class EnvNil; end
48
+ end
49
+ end
@@ -0,0 +1,26 @@
1
+ require 'destructure/destructure'
2
+
3
+ module DestructureMagic
4
+ def self.included(base)
5
+ orig = base.instance_method(:=~)
6
+
7
+ base.send(:define_method, :=~) do |pattern|
8
+ if pattern.is_a?(Regexp)
9
+ orig.bind(self).call(pattern)
10
+ elsif pattern.is_a?(Proc)
11
+ # stuff gets cranky if you try to factor this out
12
+ caller_binding = binding.of_caller(1)
13
+ caller_location = caller_locations(1,1)[0].label
14
+ caller = caller_binding.eval('self')
15
+ caller.class.send(:include, Destructure) unless caller.class.included_modules.include?(Destructure)
16
+ caller.send(:dbind_internal, self, pattern.to_sexp(strip_enclosure: true, ignore_nested: true), caller_binding, caller_location)
17
+ else
18
+ super
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ class Object; include DestructureMagic end
25
+ class String; include DestructureMagic end
26
+ class Symbol; include DestructureMagic end
@@ -0,0 +1,156 @@
1
+ require 'active_support/inflector'
2
+ require 'destructure/dmatch'
3
+
4
+ module Destructure
5
+ class SexpTransformer
6
+
7
+ def self.transform(sp, caller_binding)
8
+ SexpTransformer.new(caller_binding).transform(sp)
9
+ end
10
+
11
+ def initialize(caller_binding)
12
+ @caller_binding = caller_binding
13
+ end
14
+
15
+ def transform(sp)
16
+ _ = DMatch::_
17
+ klass_sym = DMatch::Var.new(&method(:is_constant?))
18
+ case
19
+ # '_' (wildcard)
20
+ when e = dmatch([:call, _, :_, _], sp); _
21
+ # object matcher without parameters
22
+ when e = dmatch([:const, klass_sym], sp)
23
+ make_obj(e[klass_sym], {})
24
+ # '~' (splat)
25
+ when e = dmatch([:call, var(:identifier_sexp), :~, [:arglist]], sp); splat(unwind_receivers_and_clean(e[:identifier_sexp]))
26
+ # '!' (variable value)
27
+ when e = dmatch([:not, var(:value_sexp)], sp)
28
+ @caller_binding.eval(unwind_receivers_and_clean(e[:value_sexp]).to_s)
29
+ # '|' (alternative patterns)
30
+ when e = dmatch([:call, var(:rest), :|, [:arglist, var(:alt)]], sp); DMatch::Or.new(*[e[:rest], e[:alt]].map(&method(:transform)))
31
+ # generic call
32
+ when e = dmatch([:call, var(:receiver), var(:msg), var(:arglist)], sp)
33
+ transform_call(e[:receiver], e[:msg], e[:arglist])
34
+ # instance variable
35
+ when e = dmatch([:ivar, var(:name)], sp); var(e[:name].to_s)
36
+ # let
37
+ # ... with local or instance vars
38
+ when e = dmatch([DMatch::Or.new(:lasgn, :iasgn), var(:lhs), var(:rhs)], sp)
39
+ let_var(e[:lhs], transform(e[:rhs]))
40
+ # ... with attributes or something more complicated
41
+ when e = dmatch([:attrasgn, var(:obj), var(:attr), [:arglist, var(:rhs)]], sp)
42
+ var_name = unwind_receivers_and_clean([:call, e[:obj], e[:attr].to_s.sub(/=$/,'').to_sym, [:arglist]])
43
+ let_var(var_name, transform(e[:rhs]))
44
+ # literal values
45
+ when e = dmatch([:lit, var(:value)], sp); e[:value]
46
+ when e = dmatch([:true], sp); true
47
+ when e = dmatch([:false], sp); false
48
+ when e = dmatch([:nil], sp); nil
49
+ when e = dmatch([:str, var(:s)], sp); e[:s]
50
+ when e = dmatch([:array, splat(:items)], sp); e[:items].map(&method(:transform))
51
+ when e = dmatch([:hash, splat(:kvs)], sp); Hash[*e[:kvs].map(&method(:transform))]
52
+ else; raise "Unexpected sexp: #{sp.inspect}"
53
+ end
54
+ end
55
+
56
+ private ########################################
57
+
58
+ def transform_call(*sexp_call)
59
+ sexp_receiver, sexp_msg, sexp_args = sexp_call
60
+ _ = DMatch::_
61
+ klass_sym_var = DMatch::Var.new(&method(:is_constant?))
62
+ case
63
+ # Class[...]
64
+ when e = dmatch([[:const, klass_sym_var], :[]], [sexp_receiver, sexp_msg])
65
+ field_map = make_field_map(sexp_args)
66
+ klass_sym = e[klass_sym_var]
67
+ klass_sym == :Hash ? field_map : make_obj(klass_sym, field_map)
68
+ # local variable
69
+ when e = dmatch([nil, var(:name), [:arglist]], sexp_call); var(e[:name])
70
+ # call chain (@one.two(12).three[3].four)
71
+ else; var(unwind_receivers_and_clean([:call, *sexp_call]))
72
+ end
73
+ end
74
+
75
+ def make_field_map(sexp_args)
76
+ case
77
+ # Class[a: 1, b: 2]
78
+ when e = dmatch([:arglist, [:hash, splat(:kv_sexps)]], sexp_args)
79
+ kvs = transform_many(e[:kv_sexps])
80
+ Hash[*kvs]
81
+ # Class[a, b, c]
82
+ when e = dmatch([:arglist, splat(:field_name_sexps)], sexp_args)
83
+ field_names = transform_many(e[:field_name_sexps])
84
+ Hash[field_names.map { |f| [f.name, var(f.name)] }]
85
+ else; raise 'oops'
86
+ end
87
+ end
88
+
89
+ def unwind_receivers_and_clean(receiver)
90
+ unwound = unwind_receivers(receiver).gsub(/\.$/, '').gsub(/\.\[/, '[')
91
+ identifier?(unwound) ? unwound.to_sym : unwound
92
+ end
93
+
94
+ def identifier?(x)
95
+ x =~ /^[_a-zA-Z][_0-9a-zA-Z]*$/
96
+ end
97
+
98
+ def unwind_receivers(receiver)
99
+ to_s
100
+ case
101
+ when receiver.nil?; ''
102
+ when e = dmatch([:lit, var(:value)], receiver); "#{e[:value]}."
103
+ when e = dmatch([:ivar, var(:name)], receiver); "#{e[:name]}."
104
+ when e = dmatch([:call, var(:receiver), :[], [:arglist, splat(:args)]], receiver)
105
+ unwind_receivers(e[:receiver]) + format_hash_call(e[:args])
106
+ when e = dmatch([:call, var(:receiver), var(:msg), [:arglist, splat(:args)]], receiver)
107
+ unwind_receivers(e[:receiver]) + format_method_call(e[:msg], e[:args])
108
+ else; raise 'oops'
109
+ end
110
+ end
111
+
112
+ def format_method_call(msg, args)
113
+ "#{msg}(#{transform_args(args)})".gsub(/\(\)$/, '') + '.'
114
+ end
115
+
116
+ def format_hash_call(args)
117
+ "[#{transform_args(args)}].".gsub(/\(\)$/, '')
118
+ end
119
+
120
+ def transform_args(args)
121
+ transform_many(args).map { |x| x.is_a?(Symbol) ? ":#{x}" : x.to_s }.join(', ')
122
+ end
123
+
124
+ def transform_many(xs)
125
+ xs.map(&method(:transform))
126
+ end
127
+
128
+ def make_obj(klass_sym, field_map)
129
+ DMatch::Obj.of_type(klass_sym.to_s.constantize, field_map)
130
+ end
131
+
132
+ def is_constant?(x, env=nil)
133
+ x.is_a?(Symbol) && is_uppercase?(x.to_s[0])
134
+ end
135
+
136
+ def is_uppercase?(char)
137
+ char == char.upcase
138
+ end
139
+
140
+ def dmatch(*args)
141
+ DMatch::match(*args)
142
+ end
143
+
144
+ def var(name)
145
+ DMatch::Var.new(name)
146
+ end
147
+
148
+ def let_var(name, pattern)
149
+ DMatch::Var.new(name) { |x, env| DMatch.new(env).match(pattern, x) }
150
+ end
151
+
152
+ def splat(name)
153
+ DMatch::Splat.new(name)
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,90 @@
1
+ require 'singleton'
2
+
3
+ module Predicated
4
+
5
+ def test(x, env=nil)
6
+ @pred == nil ? true : @pred.call(x, env)
7
+ end
8
+
9
+ private
10
+ attr_accessor :pred
11
+
12
+ end
13
+
14
+ class DMatch
15
+
16
+ class Var
17
+ include Predicated
18
+
19
+ attr_reader :name
20
+
21
+ def initialize(name=nil, &pred)
22
+ @name = name
23
+ self.pred = pred
24
+ end
25
+ end
26
+
27
+ class Splat < Var; end
28
+
29
+ # experimental
30
+ class FilterSplat < Splat
31
+ attr_reader :pattern
32
+
33
+ def initialize(name=nil, pattern)
34
+ super(name)
35
+ @pattern = pattern
36
+ validate_pattern
37
+ end
38
+
39
+ def validate_pattern
40
+ raise 'FilterSplat pattern cannot contain variables' if @pattern.flatten.any?{|p| p.is_a?(Var)}
41
+ end
42
+ end
43
+
44
+ # experimental
45
+ class SelectSplat < Splat
46
+ attr_reader :pattern
47
+
48
+ def initialize(name=nil, pattern)
49
+ super(name)
50
+ @pattern = pattern
51
+ end
52
+ end
53
+
54
+ class Obj
55
+ include Predicated
56
+
57
+ attr_reader :fields
58
+
59
+ def initialize(fields={}, &pred)
60
+ @fields = fields
61
+ self.pred = pred
62
+ end
63
+
64
+ def self.of_type(klass, fields={}, &pred)
65
+ Obj.new(fields) {|x| x.is_a?(klass) && (!pred || pred.call(x))}
66
+ end
67
+ end
68
+
69
+ class Pred
70
+ include Predicated
71
+
72
+ def initialize(pred_callable=nil, &pred_block)
73
+ raise 'Cannot specify both a callable and a block' if pred_callable && pred_block
74
+ self.pred = pred_callable || pred_block
75
+ end
76
+ end
77
+
78
+ class Or
79
+ attr_reader :patterns
80
+ def initialize(*patterns)
81
+ @patterns = flatten(patterns)
82
+ end
83
+
84
+ private
85
+
86
+ def flatten(ps)
87
+ ps.inject([]) {|acc, p| p.is_a?(Or) ? acc + p.patterns : acc << p}
88
+ end
89
+ end
90
+ end
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: destructure
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.12
5
+ platform: ruby
6
+ authors:
7
+ - Peter Winton
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sourcify
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 0.6.0.rc4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 0.6.0.rc4
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 4.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 4.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: binding_of_caller
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 0.7.2
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 0.7.2
55
+ - !ruby/object:Gem::Dependency
56
+ name: paramix
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 2.0.1
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 2.0.1
69
+ description: Destructuring assignment in Ruby
70
+ email:
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - lib/destructure.rb
76
+ - lib/destructure/destructure.rb
77
+ - lib/destructure/dmatch.rb
78
+ - lib/destructure/env.rb
79
+ - lib/destructure/magic.rb
80
+ - lib/destructure/sexp_transformer.rb
81
+ - lib/destructure/types.rb
82
+ homepage: http://rubygems.org/gems/destructure
83
+ licenses:
84
+ - MIT
85
+ metadata: {}
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ requirements: []
101
+ rubyforge_project:
102
+ rubygems_version: 2.2.2
103
+ signing_key:
104
+ specification_version: 4
105
+ summary: Destructuring assignment in Ruby
106
+ test_files: []