pipe_operator 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,22 @@
1
+ Gem::Specification.new do |s|
2
+ s.author = "LendingHome"
3
+ s.email = "engineering@lendinghome.com"
4
+ s.extra_rdoc_files = ["LICENSE"]
5
+ s.files = `git ls-files 2>/dev/null`.split("\n")
6
+ s.homepage = "https://github.com/lendinghome/pipe_operator"
7
+ s.license = "MIT"
8
+ s.name = "pipe_operator"
9
+ s.required_ruby_version = ">= 2.0.0"
10
+ s.summary = "Elixir/Unix style pipe operations in Ruby"
11
+ s.test_files = `git ls-files -- spec/* 2>/dev/null`.split("\n")
12
+ s.version = "0.0.1"
13
+
14
+ s.rdoc_options = %w[
15
+ --all
16
+ --charset=UTF-8
17
+ --hyperlink-all
18
+ --inline-source
19
+ --line-numbers
20
+ --main README.md
21
+ ]
22
+ end
@@ -0,0 +1,243 @@
1
+ RSpec.describe PipeOperator do
2
+ using PipeOperator
3
+
4
+ describe ".gem" do
5
+ it "returns a Gem::Specification" do
6
+ gem = PipeOperator.gem
7
+ expect(gem).to be_a(::Gem::Specification)
8
+ expect(gem.name).to eq("pipe_operator")
9
+ expect(gem.version).to be_a(::Gem::Version)
10
+ end
11
+ end
12
+
13
+ describe ".root" do
14
+ it "returns a Pathname to gem root" do
15
+ expected = ::Pathname.new(__dir__).join("..")
16
+ expect(PipeOperator.root).to match(expected)
17
+ end
18
+ end
19
+
20
+ describe ".version" do
21
+ it "returns a version string" do
22
+ expected = /^\d+\.\d+\.\d+$/
23
+ expect(PipeOperator.version).to match(expected)
24
+ end
25
+ end
26
+
27
+ describe "#pipe" do
28
+ it "doesn't break existing behavior" do
29
+ actual = Math.sqrt(9)
30
+ expect(actual).to eq(3)
31
+ end
32
+
33
+ it "returns a pipe object that responds to anything" do
34
+ actual = Math.|
35
+ expect(actual).to be_a(PipeOperator::Pipe)
36
+ expect { actual.anything }.not_to raise_error
37
+ end
38
+
39
+ it "returns a callable proc" do
40
+ pipe = Math.|
41
+ sqrt = pipe.sqrt
42
+ expect(sqrt).to be_a(::Proc)
43
+ expect(sqrt).to be_a(PipeOperator::Closure)
44
+
45
+ actual = sqrt[9]
46
+ expect(actual).to eq(3)
47
+
48
+ actual = sqrt.(9)
49
+ expect(actual).to eq(3)
50
+
51
+ actual = sqrt.call(9)
52
+ expect(actual).to eq(3)
53
+ end
54
+
55
+ it "casts to &block" do
56
+ actual = [9].map(&Math.|.sqrt)
57
+ expect(actual).to eq([3])
58
+
59
+ actual = [3].map(&2.pipe.send(:*))
60
+ expect(actual).to eq([6])
61
+ end
62
+
63
+ it "curries arguments and blocks" do
64
+ actual = "testing".|.sub("test").("TEST")
65
+ expect(actual).to eq("TESTing")
66
+
67
+ actual = ["testing"].pipe.map(&:upcase).call
68
+ expect(actual).to eq(["TESTING"])
69
+
70
+ actual = ["testing"].pipe.map(&:upcase).call(&:reverse)
71
+ expect(actual).to eq(["gnitset"])
72
+
73
+ actual = 2.pipe{Math.atan2(3)}
74
+ expect(actual).to eq(0.982793723247329)
75
+
76
+ actual = -2.pipe{abs | Math.atan2(self, 3) | to_s}
77
+ expect(actual).to eq("0.5880026035475675")
78
+ end
79
+
80
+ it "behaves like __send__ with args and no block" do
81
+ sqrt = Math | :sqrt
82
+ actual = sqrt.call(16)
83
+ expect(actual).to eq(4.0)
84
+
85
+ sqrt = Math.pipe(:sqrt, 16)
86
+ actual = sqrt.call
87
+ expect(actual).to eq(4.0)
88
+
89
+ expect { sqrt.call(16) }.to raise_error(::ArgumentError)
90
+ end
91
+
92
+ it "behaves like instance_exec with a block" do
93
+ actual = "abc".pipe { reverse }
94
+ expect(actual).to eq("cba")
95
+
96
+ actual = "abc".pipe { reverse.upcase }
97
+ expect(actual).to eq("CBA")
98
+
99
+ actual = "abc".pipe { reverse | upcase }
100
+ expect(actual).to eq("CBA")
101
+ end
102
+
103
+ it "supports calling objects on other methods" do
104
+ actual = "abc".pipe { Marshal.dump | Base64.encode64 }
105
+ expect(actual).to eq("BAhJIghhYmMGOgZFVA==\n")
106
+ end
107
+
108
+ it "supports pipe and stream expressions" do
109
+ actual = "-9".|{to_i}
110
+ expect(actual).to eq(-9)
111
+
112
+ actual = "-9".|{to_i; abs}
113
+ expect(actual).to eq(9)
114
+
115
+ actual = "-9".|{to_i; abs; Math.sqrt}
116
+ expect(actual).to eq(3)
117
+
118
+ actual = "-9".pipe { to_i | abs | Math.sqrt }
119
+ expect(actual).to eq(3)
120
+
121
+ actual = "-9".|{to_i; abs; Math.sqrt; to_i; send(:*, 2)}
122
+ expect(actual).to eq(6)
123
+
124
+ actual = "-16".|{
125
+ to_i
126
+ abs
127
+ Math.sqrt
128
+ Math.sqrt
129
+ }
130
+ expect(actual).to eq(2)
131
+
132
+ actual = ["-16", "256"].pipe do
133
+ lazy # streams
134
+ map { |n| n.to_i.abs }
135
+ map { |n| n * 2 }
136
+ end
137
+ expect(actual).to be_an(Enumerator::Lazy)
138
+ expect(actual.to_a).to eq([32, 512])
139
+ end
140
+
141
+ it "resolves recursive pipes" do
142
+ actual = ["-16", "256"].pipe do
143
+ lazy
144
+ map { |n| n.to_i.abs }
145
+ map { |n| n * 2 }
146
+ map(&Math.sqrt)
147
+ map(&Math.sqrt)
148
+ reduce(&:+)
149
+ Math.sqrt
150
+ ceil
151
+ end
152
+ expect(actual).to eq(3)
153
+ end
154
+
155
+ it "resolves pipe chain" do
156
+ pipe = Math.|.sqrt.to_i.to_s
157
+ actual = pipe.call(256)
158
+ expect(actual).to eq("16")
159
+
160
+ actual = 64.pipe{Math.sqrt.to_i.to_s}
161
+ expect(actual).to eq("8")
162
+
163
+ actual = [64].map(&Math.|.sqrt.to_i.to_s)
164
+ expect(actual).to eq(["8"])
165
+
166
+ actual = [64, 256].map(&Math.|.sqrt.to_i.to_s)
167
+ expect(actual).to eq(["8", "16"])
168
+ end
169
+
170
+ it "proxies pipe arguments", :pending do
171
+ class Markdown
172
+ def format(string)
173
+ string.upcase
174
+ end
175
+ end
176
+
177
+ actual = Markdown.new.|.format.call("test")
178
+ expect(actual).to eq("TEST")
179
+
180
+ actual = "test".pipe(Markdown.new, &:format)
181
+ expect(actual).to eq("TEST")
182
+ end
183
+
184
+ it "observes method changes" do
185
+ methods = Math.methods(false).sort
186
+ expect(methods).not_to be_empty
187
+
188
+ proxy = PipeOperator::ProxyResolver.new(Math).proxy
189
+ expect(proxy.definitions).to eq(methods)
190
+ expect(proxy.definitions).not_to include(:test)
191
+
192
+ def Math.test; end
193
+ expect(proxy.definitions).to include(:test)
194
+
195
+ Math.singleton_class.remove_method(:test)
196
+ expect(proxy.definitions).not_to include(:test)
197
+
198
+ expect{ proxy.undefine(:invalid) }.not_to raise_error
199
+ end
200
+
201
+ it "varies Pipe#inspect based on the object" do
202
+ basic = ::BasicObject.new
203
+ def basic.hash; 0 end
204
+
205
+ expected = {
206
+ ::BasicObject => "#<PipeOperator::Pipe:BasicObject>",
207
+ ::Class => "#<PipeOperator::Pipe:Class>",
208
+ ::Class.new => /#<PipeOperator::Pipe:#<Class:.+>>/,
209
+ ::Math => "#<PipeOperator::Pipe:Math>",
210
+ 123 => "#<PipeOperator::Pipe:123>",
211
+ true => "#<PipeOperator::Pipe:true>",
212
+ basic => /#<PipeOperator::Pipe:#<BasicObject:.+>>/,
213
+ }
214
+
215
+ expected.each do |object, inspect|
216
+ pipe = object.__pipe__
217
+ matcher = Regexp === inspect ? match(inspect) : eq(inspect)
218
+ expect(pipe.inspect).to matcher
219
+ end
220
+ end
221
+
222
+ it "varies ProxyResolver#inspect based on the object" do
223
+ basic = ::BasicObject.new
224
+ def basic.hash; 0 end
225
+
226
+ expected = {
227
+ ::BasicObject => "#<PipeOperator::Proxy:BasicObject>",
228
+ ::Class => "#<PipeOperator::Proxy:Class>",
229
+ ::Class.new => /#<PipeOperator::Proxy:#<Class:.+>>/,
230
+ ::Math => "#<PipeOperator::Proxy:Math>",
231
+ 123 => "#<PipeOperator::Proxy:#<Integer>>",
232
+ true => "#<PipeOperator::Proxy:#<TrueClass>>",
233
+ basic => /#<PipeOperator::Proxy:#<BasicObject:.+>>/,
234
+ }
235
+
236
+ expected.each do |object, inspect|
237
+ proxy = PipeOperator::ProxyResolver.new(object).proxy
238
+ matcher = Regexp === inspect ? match(inspect) : eq(inspect)
239
+ expect(proxy.inspect).to matcher
240
+ end
241
+ end
242
+ end
243
+ end
@@ -0,0 +1,16 @@
1
+ require "simplecov"
2
+ SimpleCov.start { add_filter("/vendor/bundle/") }
3
+
4
+ require "base64"
5
+ require "json"
6
+ require "pry"
7
+ require "pry-byebug"
8
+
9
+ ENV["PIPE_OPERATOR_FROZEN"] ||= "1"
10
+ require_relative "../lib/pipe_operator"
11
+
12
+ RSpec.configure do |config|
13
+ config.filter_run :focus
14
+ config.raise_errors_for_deprecations!
15
+ config.run_all_when_everything_filtered = true
16
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pipe_operator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - LendingHome
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-12-09 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email: engineering@lendinghome.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files:
18
+ - LICENSE
19
+ files:
20
+ - ".gitignore"
21
+ - ".rspec"
22
+ - ".rubocop.yml"
23
+ - Gemfile
24
+ - Gemfile.lock
25
+ - LICENSE
26
+ - README.md
27
+ - Rakefile
28
+ - lib/pipe_operator.rb
29
+ - lib/pipe_operator/autoload.rb
30
+ - lib/pipe_operator/closure.rb
31
+ - lib/pipe_operator/observer.rb
32
+ - lib/pipe_operator/pipe.rb
33
+ - lib/pipe_operator/proxy.rb
34
+ - lib/pipe_operator/proxy_resolver.rb
35
+ - pipe_operator.gemspec
36
+ - spec/pipe_operator_spec.rb
37
+ - spec/spec_helper.rb
38
+ homepage: https://github.com/lendinghome/pipe_operator
39
+ licenses:
40
+ - MIT
41
+ metadata: {}
42
+ post_install_message:
43
+ rdoc_options:
44
+ - "--all"
45
+ - "--charset=UTF-8"
46
+ - "--hyperlink-all"
47
+ - "--inline-source"
48
+ - "--line-numbers"
49
+ - "--main"
50
+ - README.md
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: 2.0.0
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubyforge_project:
65
+ rubygems_version: 2.7.7
66
+ signing_key:
67
+ specification_version: 4
68
+ summary: Elixir/Unix style pipe operations in Ruby
69
+ test_files:
70
+ - spec/pipe_operator_spec.rb
71
+ - spec/spec_helper.rb