destructure 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []