pipe_operator 0.0.1

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
+ SHA256:
3
+ metadata.gz: fc08d5b15937d1418ca864e631eb9f0a6a3fafd04e2c1f7496fbdd925531eec7
4
+ data.tar.gz: 340f185be7fe2f803a0d61603f1b1c89c641f6dfbf21ba691e1c3e48c3c5bdce
5
+ SHA512:
6
+ metadata.gz: 4b2e3096c092d839dde50eadbc196cef82745371c99cb313f845587b0c4908ab6225a261309984d2cdef927605f44308026adf57a7c02970721d84c0e240d958
7
+ data.tar.gz: a873ca59f03c6e50630c9d8b91f5494a5b7bfb06b558b21c574c716065f63fb381d155b66c106c4d12de237fbb8b1506bb40fd2e459f130a278ceea26ee0daa5
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ coverage
2
+ doc
3
+ pkg
4
+ rdoc
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format progress
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,116 @@
1
+ AllCops:
2
+ DisplayCopNames: true
3
+ TargetRubyVersion: 2.5
4
+ Exclude:
5
+ - "Rakefile"
6
+ - "tmp/**/*"
7
+
8
+ Gemspec/RequiredRubyVersion:
9
+ Enabled: false
10
+
11
+ Layout/AlignHash:
12
+ Exclude:
13
+ - "spec/**/*"
14
+
15
+ Layout/EmptyLineAfterGuardClause:
16
+ Enabled: false
17
+
18
+ Layout/SpaceBeforeBlockBraces:
19
+ Exclude:
20
+ - "spec/**/*"
21
+
22
+ Layout/SpaceInsideBlockBraces:
23
+ Exclude:
24
+ - "spec/**/*"
25
+
26
+ Metrics/AbcSize:
27
+ Enabled: false
28
+
29
+ Metrics/BlockLength:
30
+ Enabled: false
31
+
32
+ Metrics/CyclomaticComplexity:
33
+ Enabled: false
34
+
35
+ Metrics/LineLength:
36
+ Enabled: false
37
+
38
+ Metrics/MethodLength:
39
+ Enabled: false
40
+
41
+ Metrics/PerceivedComplexity:
42
+ Exclude:
43
+ - "lib/pipe_operator/proxy.rb"
44
+ - "lib/pipe_operator/proxy_resolver.rb"
45
+
46
+ Naming/MemoizedInstanceVariableName:
47
+ Exclude:
48
+ - "lib/pipe_operator/closure.rb"
49
+
50
+ Naming/UncommunicativeMethodParamName:
51
+ Enabled: false
52
+
53
+ Style/BlockDelimiters:
54
+ Exclude:
55
+ - "spec/**/*"
56
+
57
+ Style/CaseEquality:
58
+ Enabled: false
59
+
60
+ Style/Documentation:
61
+ Enabled: false
62
+
63
+ Style/DoubleNegation:
64
+ Enabled: false
65
+
66
+ Style/FormatString:
67
+ Enabled: false
68
+
69
+ Style/FormatStringToken:
70
+ Enabled: false
71
+
72
+ Style/FrozenStringLiteralComment:
73
+ Enabled: false
74
+
75
+ Style/GuardClause:
76
+ Enabled: false
77
+
78
+ Style/LambdaCall:
79
+ Exclude:
80
+ - "spec/**/*"
81
+
82
+ Style/MethodMissingSuper:
83
+ Exclude:
84
+ - "lib/pipe_operator/closure.rb"
85
+ - "lib/pipe_operator/pipe.rb"
86
+
87
+ Style/RedundantBegin:
88
+ Exclude:
89
+ - "lib/pipe_operator/proxy_resolver.rb"
90
+
91
+ Style/RescueModifier:
92
+ Exclude:
93
+ - "lib/pipe_operator/proxy_resolver.rb"
94
+
95
+ Style/MissingRespondToMissing:
96
+ Exclude:
97
+ - "lib/pipe_operator/closure.rb"
98
+ - "lib/pipe_operator/pipe.rb"
99
+
100
+ Style/Semicolon:
101
+ Exclude:
102
+ - "spec/**/*"
103
+
104
+ Style/SingleLineMethods:
105
+ Exclude:
106
+ - "spec/**/*"
107
+
108
+ Style/StringLiterals:
109
+ EnforcedStyle: double_quotes
110
+
111
+ Style/TrailingCommaInHashLiteral:
112
+ Exclude:
113
+ - "spec/**/*"
114
+
115
+ Style/WordArray:
116
+ Enabled: false
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source "https://www.rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem "codeclimate-test-reporter"
6
+ gem "fasterer"
7
+ gem "pry"
8
+ gem "pry-byebug"
9
+ gem "rake"
10
+ gem "rdoc"
11
+ gem "rspec"
12
+ gem "rubocop"
data/Gemfile.lock ADDED
@@ -0,0 +1,83 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ pipe_operator (0.0.1)
5
+
6
+ GEM
7
+ remote: https://www.rubygems.org/
8
+ specs:
9
+ ast (2.4.0)
10
+ byebug (10.0.2)
11
+ codeclimate-test-reporter (1.0.9)
12
+ simplecov (<= 0.13)
13
+ coderay (1.1.2)
14
+ colorize (0.8.1)
15
+ diff-lcs (1.3)
16
+ docile (1.1.5)
17
+ fasterer (0.4.1)
18
+ colorize (~> 0.7)
19
+ ruby_parser (~> 3.11.0)
20
+ jaro_winkler (1.5.1)
21
+ json (2.1.0)
22
+ method_source (0.9.2)
23
+ parallel (1.12.1)
24
+ parser (2.5.3.0)
25
+ ast (~> 2.4.0)
26
+ powerpack (0.1.2)
27
+ pry (0.12.2)
28
+ coderay (~> 1.1.0)
29
+ method_source (~> 0.9.0)
30
+ pry-byebug (3.6.0)
31
+ byebug (~> 10.0)
32
+ pry (~> 0.10)
33
+ rainbow (3.0.0)
34
+ rake (12.3.1)
35
+ rdoc (6.0.4)
36
+ rspec (3.8.0)
37
+ rspec-core (~> 3.8.0)
38
+ rspec-expectations (~> 3.8.0)
39
+ rspec-mocks (~> 3.8.0)
40
+ rspec-core (3.8.0)
41
+ rspec-support (~> 3.8.0)
42
+ rspec-expectations (3.8.2)
43
+ diff-lcs (>= 1.2.0, < 2.0)
44
+ rspec-support (~> 3.8.0)
45
+ rspec-mocks (3.8.0)
46
+ diff-lcs (>= 1.2.0, < 2.0)
47
+ rspec-support (~> 3.8.0)
48
+ rspec-support (3.8.0)
49
+ rubocop (0.60.0)
50
+ jaro_winkler (~> 1.5.1)
51
+ parallel (~> 1.10)
52
+ parser (>= 2.5, != 2.5.1.1)
53
+ powerpack (~> 0.1)
54
+ rainbow (>= 2.2.2, < 4.0)
55
+ ruby-progressbar (~> 1.7)
56
+ unicode-display_width (~> 1.4.0)
57
+ ruby-progressbar (1.10.0)
58
+ ruby_parser (3.11.0)
59
+ sexp_processor (~> 4.9)
60
+ sexp_processor (4.11.0)
61
+ simplecov (0.13.0)
62
+ docile (~> 1.1.0)
63
+ json (>= 1.8, < 3)
64
+ simplecov-html (~> 0.10.0)
65
+ simplecov-html (0.10.2)
66
+ unicode-display_width (1.4.0)
67
+
68
+ PLATFORMS
69
+ ruby
70
+
71
+ DEPENDENCIES
72
+ codeclimate-test-reporter
73
+ fasterer
74
+ pipe_operator!
75
+ pry
76
+ pry-byebug
77
+ rake
78
+ rdoc
79
+ rspec
80
+ rubocop
81
+
82
+ BUNDLED WITH
83
+ 1.17.1
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2018 LendingHome - engineering@lendinghome.com
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,384 @@
1
+ # ![LendingHome](https://avatars0.githubusercontent.com/u/5448482?s=24&v=4) pipe_operator
2
+
3
+ > Elixir/Unix style pipe operations in Ruby - **PROOF OF CONCEPT**
4
+
5
+ ```ruby
6
+ -9.pipe { abs | Math.sqrt | to_i } #=> 3
7
+ ```
8
+
9
+ ```ruby
10
+ [9, 64].map(&Math.|.sqrt.to_i) #=> [3, 8]
11
+ ```
12
+
13
+ ```ruby
14
+ "https://api.github.com/repos/ruby/ruby".| do
15
+ URI.parse
16
+ Net::HTTP.get
17
+ JSON.parse.fetch("stargazers_count")
18
+ yield_self { |n| "Ruby has #{n} stars" }
19
+ Kernel.puts
20
+ end
21
+ #=> Ruby has 15115 stars
22
+ ```
23
+
24
+ ## Why?
25
+
26
+ There's been some recent activity related to `Method` and `Proc` composition in Ruby:
27
+
28
+ * [#6284 - Add composition for procs](https://bugs.ruby-lang.org/issues/6284)
29
+ * [#13581 - Syntax sugar for method reference](https://bugs.ruby-lang.org/issues/13581)
30
+ * [#12125 - Shorthand operator for Object#method](https://bugs.ruby-lang.org/issues/12125)
31
+
32
+ This gem was created to **propose an alternative syntax** for this kind of behavior.
33
+
34
+ ## Concept
35
+
36
+ The general idea is to **pass the result of one expression as an argument to another expression** - similar to [Unix pipelines](https://en.wikipedia.org/wiki/Pipeline_(Unix)):
37
+
38
+ ```ruby
39
+ echo "testing" | sed "s/ing//" | rev
40
+ #=> tset
41
+ ```
42
+
43
+ The [Elixir pipe operator documentation](https://elixirschool.com/en/lessons/basics/pipe-operator/) has some other examples but basically it allows expressions like:
44
+
45
+ ```ruby
46
+ JSON.parse(Net::HTTP.get(URI.parse(url)))
47
+ ```
48
+
49
+ To be **inversed** and rewritten as **left to right** or **top to bottom** which is more **natural to read** in English:
50
+
51
+ ```ruby
52
+ # left to right
53
+ url.pipe { URI.parse | Net::HTTP.get | JSON.parse }
54
+
55
+ # or top to bottom for clarity
56
+ url.pipe do
57
+ URI.parse
58
+ Net::HTTP.get
59
+ JSON.parse
60
+ end
61
+ ```
62
+
63
+ The differences become a bit **clearer when other arguments are involved**:
64
+
65
+ ```ruby
66
+ loans = Loan.preapproved.submitted(Date.current).where(broker: Current.user)
67
+ data = loans.map { |loan| LoanPresenter.new(loan).as_json }
68
+ json = JSON.pretty_generate(data, allow_nan: false)
69
+ ```
70
+
71
+ Using pipes **removes the verbosity of maps and temporary variables**:
72
+
73
+ ```ruby
74
+ json = Loan.pipe do
75
+ preapproved
76
+ submitted(Date.current)
77
+ where(broker: Current.user)
78
+ map(&LoanPresenter.new.as_json)
79
+ JSON.pretty_generate(allow_nan: false)
80
+ end
81
+ ```
82
+
83
+ While the ability to perform a job correctly and efficiently is certainly important - the **true beauty of a program lies in its clarity and conciseness**:
84
+
85
+ ```ruby
86
+ "https://api.github.com/repos/ruby/ruby".| do
87
+ URI.parse
88
+ Net::HTTP.get
89
+ JSON.parse.fetch("stargazers_count")
90
+ yield_self { |n| "Ruby has #{n} stars" }
91
+ Kernel.puts
92
+ end
93
+ #=> Ruby has 15115 stars
94
+ ```
95
+
96
+ There's nothing really special here - it's just a **block of expressions like any other Ruby DSL** and the pipe `|` operator has been [around for decades](https://en.wikipedia.org/wiki/Pipeline_(Unix))!
97
+
98
+ ```ruby
99
+ # we can already do this
100
+ objects.map(&Marshal.method(:dump))
101
+
102
+ # but this is more concise
103
+ objects.map(&Marshal.|.dump)
104
+ ```
105
+
106
+ The **simplicity and elegance of Ruby** is one of the many reasons that people fall in love with the language!
107
+
108
+ ```ruby
109
+ Ruby.is.so(:simple, &elegant).that(you can) do
110
+ pretty_much ANYTHING if it.compiles!
111
+ end
112
+ ```
113
+
114
+ This concept of **pipes could be a great fit** like it has been for many other languages:
115
+
116
+ * [Caml composition operators](http://caml.inria.fr/pub/docs/manual-ocaml/libref/Pervasives.html#1_Compositionoperators)
117
+ * [Closure threading macros](https://clojure.org/guides/threading_macros)
118
+ * [Elixir pipe operator](https://elixirschool.com/en/lessons/basics/pipe-operator/)
119
+ * [Elm operators](https://elm-lang.org/docs/syntax#operators)
120
+ * [F# function composition and pipelining](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/functions/index#function-composition-and-pipelining)
121
+ * [Hack pipe operator](https://docs.hhvm.com/hack/operators/pipe-operator)
122
+ * [Haskell pipes](http://hackage.haskell.org/package/pipes-4.3.9/docs/Pipes-Tutorial.html)
123
+ * [JavaScript pipeline operator proposals](https://github.com/tc39/proposal-pipeline-operator/wiki)
124
+ * [LiveScript piping](http://livescript.net/#piping)
125
+ * [Unix pipelines](https://en.wikipedia.org/wiki/Pipeline_(Unix))
126
+
127
+ ## Usage
128
+
129
+ **WARNING - EXPERIMENTAL PROOF OF CONCEPT**
130
+
131
+ This has only been **tested in isolation with RSpec**!
132
+
133
+ ```ruby
134
+ # First `gem install pipe_operator`
135
+ require "pipe_operator"
136
+
137
+ # Then use PipeOperator as a refinement
138
+ using ::PipeOperator
139
+
140
+ # Or include PipeOperator in classes/modules
141
+ class AnyClass
142
+ include ::PipeOperator
143
+ end
144
+
145
+ # Or monkey patch PipeOperator into ALL objects
146
+ require "pipe_operator/autoload"
147
+ ```
148
+
149
+ ## Implementation
150
+
151
+ The [PipeOperator](https://github.com/lendinghome/pipe_operator/blob/master/lib/pipe_operator.rb) module has a method named `__pipe__` which is aliased as `pipe` for convenience and `|` for syntactic sugar:
152
+
153
+ ```ruby
154
+ def __pipe__(*args, &block)
155
+ Pipe.new(self, *args, &block)
156
+ end
157
+
158
+ alias | __pipe__
159
+ alias pipe __pipe__
160
+ ```
161
+
162
+ When no arguments are passed to `__pipe__` then a [PipeOperator::Pipe](https://github.com/lendinghome/pipe_operator/blob/master/lib/pipe_operator/pipe.rb) object is returned:
163
+
164
+ ```ruby
165
+ Math.| #=> #<PipeOperator::Pipe:Math>
166
+ ```
167
+
168
+ Any methods invoked on this object returns a [PipeOperator::Closure](https://github.com/lendinghome/pipe_operator/blob/master/lib/pipe_operator/closure.rb) which **calls the method on the object later**:
169
+
170
+ ```ruby
171
+ sqrt = Math.|.sqrt #=> #<PipeOperator::Closure:0x00007fc1172ed558@pipe_operator/closure.rb:18>
172
+ sqrt.call(16) #=> 4.0
173
+
174
+ missing = Math.|.missing #=> #<PipeOperator::Closure:0x00007fc11726f0e0@pipe_operator/closure.rb:18>
175
+ missing.call #=> NoMethodError: undefined method 'missing' for Math:Module
176
+
177
+ Math.method(:missing) #=> NameError: undefined method 'missing' for class '#<Class:Math>'
178
+ ```
179
+
180
+ When `__pipe__` is called **with arguments but without a block** then it behaves similar to `__send__`:
181
+
182
+ ```ruby
183
+ sqrt = Math | :sqrt #=> #<PipeOperator::Closure:0x00007fe52e0cdf80@pipe_operator/closure.rb:18>
184
+ sqrt.call(16) #=> 4.0
185
+
186
+ sqrt = Math.pipe(:sqrt, 16) #=> #<PipeOperator::Closure:0x00007fe52fa18fd0@pipe_operator/closure.rb:18>
187
+ sqrt.call #=> 4.0
188
+ sqrt.call(16) #=> ArgumentError: wrong number of arguments (given 2, expected 1)
189
+ ```
190
+
191
+ These [PipeOperator::Closure](https://github.com/lendinghome/pipe_operator/blob/master/lib/pipe_operator/closure.rb) objects can be [bound as block arguments](https://github.com/lendinghome/pipe_operator/blob/master/lib/pipe_operator/proxy.rb#L10-L13) just like any other [Proc](https://ruby-doc.org/core-2.5.3/Proc.html):
192
+
193
+ ```ruby
194
+ [16, 256].map(&Math.|.sqrt) #=> [4.0, 16.0]
195
+ ```
196
+
197
+ Simple **closure composition is supported** via [method chaining](https://github.com/lendinghome/pipe_operator/blob/master/lib/pipe_operator/closure.rb#L56):
198
+
199
+ ```ruby
200
+ [16, 256].map(&Math.|.sqrt.to_i.to_s) #=> ["4", "16"]
201
+ ```
202
+
203
+ The **block** form of `__pipe__` behaves **similar to instance_exec** but can also [call methods on other objects](https://github.com/lendinghome/pipe_operator/blob/master/lib/pipe_operator/pipe.rb#L81):
204
+
205
+ ```ruby
206
+ "abc".pipe { reverse } #=> "cba"
207
+ "abc".pipe { reverse.upcase } #=> "CBA"
208
+
209
+ "abc".pipe { Marshal.dump } #=> "\x04\bI\"\babc\x06:\x06ET"
210
+ "abc".pipe { Marshal.dump | Base64.encode64 } #=> "BAhJIghhYmMGOgZFVA==\n"
211
+ ```
212
+
213
+ Outside the context of a `__pipe__` block things behave like normal:
214
+
215
+ ```ruby
216
+ Math.sqrt #=> ArgumentError: wrong number of arguments (given 0, expected 1)
217
+ Math.sqrt(16) #=> 4.0
218
+ ```
219
+
220
+ But within a `__pipe__` block the `Math.sqrt` expression returns a [PipeOperator::Closure](https://github.com/lendinghome/pipe_operator/blob/master/lib/pipe_operator/closure.rb) instead:
221
+
222
+ ```ruby
223
+ 16.pipe { Math.sqrt } #=> 4.0
224
+ 16.pipe { Math.sqrt(16) } #=> ArgumentError: wrong number of arguments (given 2, expected 1)
225
+ ```
226
+
227
+ The **piped object is passed as the first argument by default** but can be customized by specifying `self`:
228
+
229
+ ```ruby
230
+ class String
231
+ def self.join(*args, with: "")
232
+ args.map(&:to_s).join(with)
233
+ end
234
+ end
235
+
236
+ "test".pipe { String.join("123", with: "-") } #=> "test-123"
237
+
238
+ "test".pipe { String.join("123", self, with: "-") } #=> "123-test"
239
+ ```
240
+
241
+ Instance methods like `reverse` below [do not receive the piped object](https://github.com/lendinghome/pipe_operator/blob/master/lib/pipe_operator/pipe.rb#L79) as [an argument](https://github.com/lendinghome/pipe_operator/blob/master/lib/pipe_operator/closure.rb#L47) since it's available as `self`:
242
+
243
+ ```ruby
244
+ Base64.encode64(Marshal.dump("abc").reverse) #=> "VEUGOgZjYmEIIkkIBA==\n"
245
+
246
+ "abc".pipe { Marshal.dump | reverse | Base64.encode64 } #=> "VEUGOgZjYmEIIkkIBA==\n"
247
+ ```
248
+
249
+ Pipes also support **multi-line blocks for clarity**:
250
+
251
+ ```ruby
252
+ "abc".pipe do
253
+ Marshal.dump
254
+ reverse
255
+ Base64.encode64
256
+ end
257
+ ```
258
+
259
+ Notice the pipe `|` operator wasn't used to **separate expressions** - it's actually always optional:
260
+
261
+ ```ruby
262
+ # this example from above
263
+ "abc".pipe { Marshal.dump | reverse | Base64.encode64 }
264
+
265
+ # could also be written as
266
+ "abc".pipe { Marshal.dump; reverse; Base64.encode64 }
267
+ ```
268
+
269
+ The closures created by these **pipe expressions are evaluated via reduce**:
270
+
271
+ ```ruby
272
+ pipeline = [
273
+ -> object { Marshal.dump(object) },
274
+ -> object { object.reverse },
275
+ -> object { Base64.encode64(object) },
276
+ ]
277
+
278
+ pipeline.reduce("abc") do |object, pipe|
279
+ pipe.call(object)
280
+ end
281
+ ```
282
+
283
+ [Intercepting methods](https://github.com/lendinghome/pipe_operator/blob/master/lib/pipe_operator/proxy.rb#L19-L25) within pipes requires [prepending](https://github.com/lendinghome/pipe_operator/blob/master/lib/pipe_operator/pipe.rb#L38) a [PipeOperator::Proxy](https://github.com/lendinghome/pipe_operator/blob/master/lib/pipe_operator/proxy.rb) module infront of `::Object` and all [nested constants](https://github.com/lendinghome/pipe_operator/blob/master/lib/pipe_operator/proxy_resolver.rb#L46):
284
+
285
+ ```ruby
286
+ define_method(method) do |*args, &block|
287
+ if Pipe.open
288
+ Pipe.new(self).__send__(method, *args, &block)
289
+ else
290
+ super(*args, &block)
291
+ end
292
+ end
293
+ ```
294
+
295
+ These proxy modules are prepended everywhere! It's certainly something that **could be way more efficient as a core part of Ruby**.
296
+
297
+ ## Bugs
298
+
299
+ This test case doesn't work yet - seems like the [object is not proxied](https://github.com/lendinghome/pipe_operator/blob/master/lib/pipe_operator/pipe.rb#L39) for some reason:
300
+
301
+ ```ruby
302
+ class Markdown
303
+ def format(string)
304
+ string.upcase
305
+ end
306
+ end
307
+
308
+ "test".pipe(Markdown.new, &:format) # expected "TEST"
309
+ #=> ArgumentError: wrong number of arguments (given 0, expected 1)
310
+ ```
311
+
312
+ ## Caveats
313
+
314
+ * `PIPE_OPERATOR_AUTOLOAD`
315
+ * Constants flagged for autoload are NOT proxied by default (for performance)
316
+ * Set `ENV["PIPE_OPERATOR_AUTOLOAD"] = 1` to enable this behavior
317
+ * `PIPE_OPERATOR_FROZEN`
318
+ * Objects flagged as frozen are NOT proxied by default
319
+ * Set `ENV["PIPE_OPERATOR_FROZEN"] = 1` to enable this behavior (via [Fiddle](http://ruby-doc.org/stdlib-2.5.3/libdoc/fiddle/rdoc/Fiddle.html))
320
+ * `PIPE_OPERATOR_REBIND`
321
+ * `Object` and its recursively nested `constants` are only proxied ONCE by default (for performance)
322
+ * Constants defined after `__pipe__` is called for the first time are NOT proxied
323
+ * Set `ENV["PIPE_OPERATOR_REBIND"] = 1` to enable this behavior
324
+ * `PIPE_OPERATOR_RESERVED`
325
+ * The following methods are reserved on `PipeOperator::Closure` objects:
326
+ * `==`
327
+ * `[]`
328
+ * `__chain__`
329
+ * `__send__`
330
+ * `__shift__`
331
+ * `call`
332
+ * `class`
333
+ * `kind_of?`
334
+ * The following methods are reserved on `PipeOperator::Pipe` objects:
335
+ * `!`
336
+ * `!=`
337
+ * `==`
338
+ * `__call__`
339
+ * `__id__`
340
+ * `__pop__`
341
+ * `__push__`
342
+ * `__send__`
343
+ * `instance_exec`
344
+ * `method_missing`
345
+ * `|`
346
+ * These methods can be piped via `send` as a workaround:
347
+ * `9.pipe { Math.sqrt.to_s.send(:[], 0) }`
348
+ * `example.pipe { send(:__call__, 1, 2, 3) }`
349
+ * `example.pipe { send(:instance_exec) { } }`
350
+
351
+ ## Testing
352
+
353
+ ```bash
354
+ bundle exec rspec
355
+ ```
356
+
357
+ ## Inspiration
358
+
359
+ * https://github.com/hopsoft/pipe_envy
360
+ * https://github.com/akitaonrails/chainable_methods
361
+ * https://github.com/kek/pipelining
362
+ * https://github.com/k-motoyan/shelike-pipe
363
+ * https://github.com/nwtgck/ruby_pipe_chain
364
+ * https://github.com/teamsnap/pipe-ruby
365
+ * https://github.com/danielpclark/elixirize
366
+ * https://github.com/tiagopog/piped_ruby
367
+ * https://github.com/jicksta/methodphitamine
368
+ * https://github.com/jicksta/superators
369
+
370
+ ## Contributing
371
+
372
+ * Fork the project.
373
+ * Make your feature addition or bug fix.
374
+ * Add tests for it. This is important so we don't break it in a future version unintentionally.
375
+ * Commit, do not mess with the version or history.
376
+ * Open a pull request. Bonus points for topic branches.
377
+
378
+ ## Authors
379
+
380
+ * [Sean Huber](https://github.com/shuber)
381
+
382
+ ## License
383
+
384
+ [MIT](https://github.com/lendinghome/pipe_operator/blob/master/LICENSE) - Copyright © 2018 LendingHome