pied_piper 0.1.1 → 0.1.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 87334c647e9be342839edbef72429d045ed689e4d4279493829e157de81df18a
4
- data.tar.gz: 39a736f7f6c659f22ee63e5742e6109ddbec69d4c1550ea7a862e52fac9cb646
3
+ metadata.gz: c1caeb762311438ceb233ae23503f78187a8c832d334f3d82ce9937401c9e476
4
+ data.tar.gz: 4b59343cd11ed7aa09799fb7b60064a6a4ee017f4d604d647fcde098e73bbec1
5
5
  SHA512:
6
- metadata.gz: 86ae9191ce94fe5ca8699a43818b6ad640fd5e164208242d51b527e5449247b5b91cc2401530d85d444ed34323ef9203ae6146c596c36bc9b56c77de6d50b3d4
7
- data.tar.gz: 5721440aa08e28016e20985f009135f3388cc6d944aab30cba66bd44ebc0c7c66f3c749f64cec0692ade4d5c3712009fe162b01b291753ba62763d23035c9caa
6
+ metadata.gz: fd9a93c163394ff8843b3026772c597db9a8f088806e38d7d9f550126cfcf648ec3ece2fd4d4cb30705155b9004f3be667c6244ee448b8a8567c128ea511ce72
7
+ data.tar.gz: ffa15ea30fc2fbb48c36e5921c4217fd4978a58848cf963ce6ab46d5ae9716b27cc4b477ae0996c12178fda757c418dbc1c238ad300d817f378ca1599e82f4bf
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pied_piper (0.1.1)
4
+ pied_piper (0.1.2)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,10 +1,12 @@
1
- # PiedPiper - Don't let him lure you away...
1
+ # PiedPiper
2
2
 
3
3
  This gem provides "Unix-like" pipe functionality in Ruby.
4
4
 
5
- Another inspiration for this gem were `|>` pipes and functional programming in [Elixir](https://elixir-lang.org).
5
+ The inspiration for this gem were `|>` pipes and functional programming in [Elixir](https://elixir-lang.org).
6
6
 
7
- If you want to read about the inspiration for the name `PiedPiper`, it's an old german [fairy tale](https://en.wikipedia.org/wiki/Pied_Piper_of_Hamelin), of a guy who played pipe and hypnotized and lured all children out of town with his music and they were never be seen again.
7
+ After trying to introduce the same `|>` pipe operator in Ruby, which I found out isn't possible due to syntactic reasons ( without hacking the underlying C code ), I settled for another well-known pipe operator, the `|` Unix pipe operator.
8
+
9
+ If you want to read about the inspiration for the name `PiedPiper`, it's an old german fairy tale, the [Pied Piper of Hamelin](https://en.wikipedia.org/wiki/Pied_Piper_of_Hamelin), of a guy who played pipe and hypnotized and lured all children out of town with his music and they were never be seen again.
8
10
 
9
11
  Despite the word "pipe" there's also another common thing between the fairy tale and pipes in this gem:
10
12
 
@@ -12,7 +14,7 @@ There's a "piper" object who lures "children" (other objects) away ( through pip
12
14
 
13
15
  If you never worked with pipes, this little analogy may help to understand what's happening.
14
16
 
15
- Have fun with PiedPiper and don't let him lure you away :-)
17
+ Have fun with PiedPiper and don't let him lure you away... :-)
16
18
 
17
19
  ## Installation
18
20
 
@@ -37,56 +39,80 @@ First you have to initialize a piper object with another object of your choice:
37
39
  ```ruby
38
40
  require "pied_piper"
39
41
 
40
- piper = PiedPiper.new("rats and children")
42
+ p = PiedPiper.new("rats and kids")
41
43
  ```
42
44
 
43
45
  A shortcut to get a piper object anywhere you want, is shown in the following example. Only use this if your name is "Chuck Norris of Hamelin", since it "roundhouse pipes" (monkey-patches) Ruby's `Kernel` module :-D
44
46
 
47
+ Note: It only adds the `piper` method to `Kernel` which isn't defined anywhere else and doesn't change existing Ruby behaviour, so using it doesn't actually make you this badass ;-)
48
+
45
49
  ```ruby
46
50
  require 'pied_piper/kernel'
47
51
 
48
- piper = pipe("rats and children")
52
+ p = piper("rats and kids")
49
53
  ```
50
54
 
51
55
  Once you have a piper object you can pipe it "Unix-style".
52
56
 
53
- Our initial object (e.g: `"rats and children"`), is passed around through each pipe from left to right and transformed by one of the following objects:
57
+ Our initial object (e.g: `"rats and kids"`), is passed around through each pipe from left to right and transformed by one of the following objects:
54
58
 
55
59
  ### Symbol/String Pipes
56
60
 
57
61
  1. A Symbol or String, this calls a method on the piped object with the same name:
58
62
 
59
63
  ```ruby
60
- p = pipe("rats and children")
64
+ p = piper("rats and kids")
65
+
61
66
  p | :upcase | p.end
62
- # => "RATS AND CHILDREN"
67
+ # => "RATS AND KIDS"
63
68
  ```
64
69
 
65
- Note: Since "piping" is not a native Ruby syntax feature, rather than a method call in disguise (e.g: `p.|(:upcase)`), the underlying `PiedPiper` class, which wraps the initial object (e.g: `"foo"`) and on which the pipe method is called, is just a wrapper for the initial object which handles piping logic.
70
+ Pipes can be chained:
71
+
72
+ ```ruby
73
+ p = piper("rats and kids")
74
+
75
+ p | :upcase | :reverse | p.end
76
+ # => "SDIK DNA STAR"
77
+ ```
66
78
 
67
- Everytime you "pipe" you create a new object of class `PiedPiper`, which wraps the mutated inital object again (e.g: from `"foo"` to `"FOO"`).
79
+ ### Ending Pipes
68
80
 
69
- At the end of each pipe-chain, the piped object has to be "unwrapped" again by ending the pipe-chain with the `.end` method on the pipe object or by just writing `PiedPiper::EndOfPipe` or if you have required `pied_piper/kernel` you can just write `pipe_end`.
81
+ Note: Since "piping" is not a native Ruby syntax feature, rather than a method call in disguise (e.g: `p.|(:upcase)`), the underlying `PiedPiper` class, which wraps the initial object (e.g: `"foo"`) and on which the pipe functionality is called, is just a wrapper for the initial object which handles piping logic.
82
+
83
+ Everytime you finished a pipe transformation you create a new object of class `PiedPiper`, which wraps the mutated inital object again (e.g: from `"foo"` to `"FOO"`).
84
+
85
+ We can build pipe-chains this way, but at the end of each pipe-chain, the piped object has to be "unwrapped" again by ending the pipe-chain with the `.end` method on the pipe object or by just writing `PiedPiper::EndOfPipe` or if you have required `pied_piper/kernel` you can just write `p_end`.
70
86
 
71
87
  ```ruby
72
- p = pipe("rats and children")
88
+ p = piper("rats and kids")
73
89
 
74
90
  p | :upcase | p.end
75
- # => "RATS AND CHILDREN"
91
+ # => "RATS AND KIDS"
76
92
 
77
93
  p | :upcase | PiedPiper::EndOfPipe
78
- # => "RATS AND CHILDREN"
94
+ # => "RATS AND KIDS"
79
95
 
80
- p | :upcase | pipe_end # when PiedPiper::Kernel was required
81
- # => "RATS AND CHILDREN"
96
+ p | :upcase | p_end # when PiedPiper::Kernel was required
97
+ # => "RATS AND KIDS"
82
98
  ```
83
99
 
100
+ It would be possible to avoid writing `p.end` at the end of the chain, by implementing the gem in another way, but that would have included to monkey-patch existing Ruby classes, since the `|` method is already implemented by some of them.
101
+
102
+ I decided against that, since I only wanted to add pipe functionality and not alter existing behaviour in any way.
103
+
104
+ Thus the `PiedPiper` class was born.
105
+
106
+ If you want to add pipe functionality everywhere, we already talked about how to implement it above under "Usage".
107
+
108
+ This will make you use pipe functionality everywhere with the least amount of side-effects, since it only adds and doesn't alter existing behaviour.
109
+
84
110
  ### Array Pipes
85
111
 
86
112
  An Array, whose first element (Symbol/String) again acts as a method call on the piped object and additonal elements which act as parameters to the method call.
87
113
 
88
114
  ```ruby
89
- p = pipe("Pied Piper")
115
+ p = piper("Pied Piper")
90
116
 
91
117
  concat = [:concat, " of", " Hamelin"]
92
118
 
@@ -97,39 +123,39 @@ p | concat | p.end
97
123
  ### Proc Object Pipes
98
124
 
99
125
  An Object of `Proc` class which takes exactly one parameter:
100
- - `Proc.new { |child| ... }`
101
- - `proc { |child| ... }`
102
- - `lambda { |child| ... }`
103
- - `->(child) { ... }`
126
+ - `Proc.new { |kid| ... }`
127
+ - `proc { |kid| ... }`
128
+ - `lambda { |kid| ... }`
129
+ - `->(kid) { ... }`
104
130
 
105
131
  ```ruby
106
- p = pipe("Hypnotized child")
132
+ p = piper("Hypnotized kid")
107
133
 
108
- no_happy_end = ->(child) { child + " was never seen again" }
134
+ no_happy_end = ->(kid) { kid + " was never seen again..." }
109
135
 
110
136
  p | no_happy_end | p.end
111
- # => "Hypnotized child was never seen again"
137
+ # => "Hypnotized kid was never seen again..."
112
138
  ```
113
139
 
114
- ### Method Object Pipe
140
+ ### Method Object Pipes
115
141
 
116
142
  An object of the `Method` class, where the piped object will be used as the first parameter. You can pass an Array if you need additional parameters:
117
143
 
118
144
  ```ruby
119
145
  class PiedPiperOfHamelin
120
- def self.plays_song_on_pipe(audience = "Children", effect = "slightly")
121
- puts "#{audience} feel already #{effect + " "}hypnotized!"
146
+ def self.plays_song_on_pipe(audience = "Kids", effect = "slightly")
147
+ puts "#{audience} already feel #{effect + " "}hypnotized!"
122
148
  end
123
149
  end
124
150
 
125
- p = pipe("You")
151
+ p = piper("You")
126
152
  hypnotize = PiedPiperOfHamelin.method(:plays_song_on_pipe)
127
153
 
128
154
  p | hypnotize | p.end
129
- # => "You feel already slightly hypnotized!"
155
+ # => "You already feel slightly hypnotized!"
130
156
 
131
157
  p | [hypnotize, "VERY"] | p.end
132
- # => "You feel already VERY hypnotized!"
158
+ # => "You already feel VERY hypnotized!"
133
159
  ```
134
160
 
135
161
  ### Combining Pipes
@@ -137,26 +163,48 @@ p | [hypnotize, "VERY"] | p.end
137
163
  Once you know the basic building blocks, you can combine them and build your own pipes of arbitrary length:
138
164
 
139
165
  ```ruby
140
- piper = pipe('You')
166
+ p = piper('You')
141
167
  lures = [:+, " feel"]
142
168
  you = ->(str) { str + " hypnotized!" }
143
169
  away = :upcase
144
170
 
145
- piper | lures | you | away | h.end
171
+ p | lures | you | away | piper.end
146
172
  # => "YOU FEEL HYPNOTIZED!"
147
173
  ```
148
174
 
149
175
  ### Multiline Pipes
150
176
 
151
- Sadly Ruby's syntax doesn't allow for totally nice multiline pipes ( at least not in a way I already found out ). Thus multiline pipes have to be written like this ( since they're actually just inline syntactic sugar for methods like `obj.|(arg)` under the hood:
177
+ Sadly Ruby's syntax doesn't allow for totally nice multiline pipes ( at least not in a way I already found out ). Thus multiline pipes have to be written with a twist ( since they're actually just inline syntactic sugar for methods like `obj.|(arg)` under the hood:
178
+
179
+ #### With Backslashes:
180
+
181
+ In order to tell Ruby that our (inline) expression hasn't ended yet, when writing it over multiple lines, we can put a backslash at the end of a line to avoid syntax errors:
182
+
183
+ ```ruby
184
+ p = piper('You')
185
+ lures = [:+, " feel"]
186
+ you = ->(str) { str + " hypnotized!" }
187
+ away = :upcase
188
+
189
+ p \
190
+ | lures \
191
+ | you \
192
+ | away \
193
+ | h.end
194
+ # => "YOU FEEL HYPNOTIZED!"
195
+ ```
196
+
197
+ #### As explicit method calls:
198
+
199
+ As we already noticed that pipes `|` are simply method calls in disguise, we can explicitly call them on subsequent lines, which is valid Ruby syntax:
152
200
 
153
201
  ```ruby
154
- piper = pipe('You')
202
+ p = piper('You')
155
203
  lures = [:+, " feel"]
156
204
  you = ->(str) { str + " hypnotized!" }
157
205
  away = :upcase
158
206
 
159
- piper
207
+ p
160
208
  .|(lures)
161
209
  .|(you)
162
210
  .|(away).
@@ -164,6 +212,60 @@ piper
164
212
  # => "YOU FEEL HYPNOTIZED!"
165
213
  ```
166
214
 
215
+ ### What kind of advantages can pipes offer?
216
+
217
+ Actually we can do nothing else with pipes then we can also do with regular Ruby syntax ( since it only builds on already existing Ruby functionality and is not a totally new language feature ).
218
+
219
+ But piping objects can make some operations more clear/readable because we go from left to right in a linear fashion, instead from inside to outside like in regular function calls.
220
+
221
+ This offers advantages when for example working in "functional style" instead of using methods:
222
+
223
+ ```ruby
224
+ class Foo
225
+ def self.one
226
+ -> {|x| ... }
227
+ end
228
+ end
229
+
230
+ class Bar
231
+ def self.two
232
+ -> {|x| ... }
233
+ end
234
+ end
235
+
236
+ class Baz
237
+ def self.three
238
+ -> {|x| ... }
239
+ end
240
+ end
241
+
242
+ # This
243
+
244
+ Baz.three.call(Bar.two.call(Foo.one.call(x)))
245
+
246
+ # becomes this
247
+
248
+ piper(x) | Foo.one | Bar.two | Baz.three | p_end
249
+
250
+ # or
251
+
252
+ piper(x) \
253
+ | Foo.one \
254
+ | Bar.two \
255
+ | Baz.three \
256
+ | p_end
257
+ ```
258
+
259
+ I guess you won't use these kind of functional programming in Ruby too often, since Ruby follows another philosophy.
260
+
261
+ PiedPiper is more a kind of experiment how Ruby can be modified to resemble concepts used in other programming languages like for example [Elixir](https://elixir-lang.org).
262
+
263
+ As far as I can judge Ruby does quite a good job, the flexible language constructs that Ruby has to offer, makes it one of the nicest programming languages to work with. :-)
264
+
265
+ What other kind of good use cases for pipes can you come up with?
266
+
267
+ If you know some ( or missing features ), feel free to open up a pull request, so we can augment code/documentation :-)
268
+
167
269
  ## Development
168
270
 
169
271
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -1,10 +1,10 @@
1
1
  module PiedPiper::Kernel
2
2
  ::Kernel.class_eval do
3
- def pipe(obj = nil)
3
+ def piper(obj)
4
4
  PiedPiper.new(obj)
5
5
  end
6
6
 
7
- def pipe_end
7
+ def p_end
8
8
  PiedPiper::EndOfPipe
9
9
  end
10
10
  end
@@ -1,3 +1,3 @@
1
1
  class PiedPiper
2
- VERSION = '0.1.1'.freeze
2
+ VERSION = '0.1.2'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pied_piper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christoph Weegen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-04-10 00:00:00.000000000 Z
11
+ date: 2019-04-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler