pipe_operator 0.0.1

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,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