web_pipe 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 617c24131c1091c3ec7fb214f8f90af0f6fd199d2684ee8b71e08ac373f9795a
4
+ data.tar.gz: 68a73cfff54e49580ea8426265579ece63e3abbe78dd93b84862cee86017ba19
5
+ SHA512:
6
+ metadata.gz: aee8034e0eff973a91df4bed368f459cd841a3ea7de448b584bfef7f7d995f519ea3201bfd3f16290fb6692bf5fe8ab9bfe5d37d443eeb75581127adc3de3cee
7
+ data.tar.gz: 452773b224c0affce8aeed40251f25835cfea045208d4ba92aeb076d2a3b888df6171cfb7d5323c8d16b5ab8eb830d649d8add14fc17b1c24339957266d7de65
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,10 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4
5
+ - 2.5
6
+ - 2.6
7
+ before_install:
8
+ - gem update --system --no-doc
9
+ script:
10
+ - bundle exec rspec
@@ -0,0 +1,8 @@
1
+ --title 'web_pipe'
2
+ --embed-mixins
3
+ --output doc
4
+ --readme README.md
5
+ --files CHANGELOG.md
6
+ --markup markdown
7
+ --markup-provider=redcarpet
8
+ lib/**/*.rb
@@ -0,0 +1,9 @@
1
+ # Change Log
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](http://keepachangelog.com/)
5
+ and this project adheres to [Semantic Versioning](http://semver.org/).
6
+
7
+ ## [0.0.1] - 2019-05-07
8
+ ### Added
9
+ - Initial release.
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in web_pipe.gemspec
6
+ gemspec
@@ -0,0 +1,91 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ web_pipe (0.1.0)
5
+ dry-initializer (~> 3.0)
6
+ dry-monads (~> 1.2)
7
+ dry-struct (~> 1.0)
8
+ dry-types (~> 1.0)
9
+ rack (~> 2.0)
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ byebug (11.0.0)
15
+ coderay (1.1.2)
16
+ concurrent-ruby (1.1.5)
17
+ diff-lcs (1.3)
18
+ dry-configurable (0.8.2)
19
+ concurrent-ruby (~> 1.0)
20
+ dry-core (~> 0.4, >= 0.4.7)
21
+ dry-container (0.7.0)
22
+ concurrent-ruby (~> 1.0)
23
+ dry-configurable (~> 0.1, >= 0.1.3)
24
+ dry-core (0.4.7)
25
+ concurrent-ruby (~> 1.0)
26
+ dry-equalizer (0.2.2)
27
+ dry-inflector (0.1.2)
28
+ dry-initializer (3.0.1)
29
+ dry-logic (1.0.0)
30
+ concurrent-ruby (~> 1.0)
31
+ dry-core (~> 0.2)
32
+ dry-equalizer (~> 0.2)
33
+ dry-monads (1.2.0)
34
+ concurrent-ruby (~> 1.0)
35
+ dry-core (~> 0.4, >= 0.4.4)
36
+ dry-equalizer
37
+ dry-struct (1.0.0)
38
+ dry-core (~> 0.4, >= 0.4.3)
39
+ dry-equalizer (~> 0.2)
40
+ dry-types (~> 1.0)
41
+ ice_nine (~> 0.11)
42
+ dry-types (1.0.0)
43
+ concurrent-ruby (~> 1.0)
44
+ dry-container (~> 0.3)
45
+ dry-core (~> 0.4, >= 0.4.4)
46
+ dry-equalizer (~> 0.2, >= 0.2.2)
47
+ dry-inflector (~> 0.1, >= 0.1.2)
48
+ dry-logic (~> 1.0)
49
+ ice_nine (0.11.2)
50
+ method_source (0.9.2)
51
+ pry (0.12.2)
52
+ coderay (~> 1.1.0)
53
+ method_source (~> 0.9.0)
54
+ pry-byebug (3.7.0)
55
+ byebug (~> 11.0)
56
+ pry (~> 0.10)
57
+ rack (2.0.6)
58
+ rack-test (1.1.0)
59
+ rack (>= 1.0, < 3)
60
+ rake (10.5.0)
61
+ redcarpet (3.4.0)
62
+ rspec (3.8.0)
63
+ rspec-core (~> 3.8.0)
64
+ rspec-expectations (~> 3.8.0)
65
+ rspec-mocks (~> 3.8.0)
66
+ rspec-core (3.8.0)
67
+ rspec-support (~> 3.8.0)
68
+ rspec-expectations (3.8.2)
69
+ diff-lcs (>= 1.2.0, < 2.0)
70
+ rspec-support (~> 3.8.0)
71
+ rspec-mocks (3.8.0)
72
+ diff-lcs (>= 1.2.0, < 2.0)
73
+ rspec-support (~> 3.8.0)
74
+ rspec-support (3.8.0)
75
+ yard (0.9.19)
76
+
77
+ PLATFORMS
78
+ ruby
79
+
80
+ DEPENDENCIES
81
+ bundler (~> 1.17)
82
+ pry-byebug
83
+ rack-test (~> 1.1)
84
+ rake (~> 10.0)
85
+ redcarpet (~> 3.4)
86
+ rspec (~> 3.0)
87
+ web_pipe!
88
+ yard (~> 0.9)
89
+
90
+ BUNDLED WITH
91
+ 1.17.2
@@ -0,0 +1,279 @@
1
+ [![Gem Version](https://badge.fury.io/rb/web_pipe.svg)](https://badge.fury.io/rb/web_pipe)
2
+ [![Build Status](https://travis-ci.com/waiting-for-dev/web_pipe.svg?branch=master)](https://travis-ci.com/waiting-for-dev/web_pipe)
3
+
4
+ # WebPipe
5
+
6
+ `web_pipe` is a rack application builder through a pipe of operations
7
+ applied to an immutable struct.
8
+
9
+ You can also think of it as a web controllers builder (the C in `MVC`)
10
+ totally declouped from the web routing (which you can still do with
11
+ something like [`hanami-router`](https://github.com/hanami/router),
12
+ [`http_router`](https://github.com/joshbuddy/http_router) or plain
13
+ [rack's `map`
14
+ method](https://www.rubydoc.info/github/rack/rack/Rack/Builder#map-instance_method)).
15
+
16
+ If you are familiar with rack you know that it models a two-way pipe,
17
+ where each middleware in the stack has the chance to modify the
18
+ request before it arrives to the actual application, and the response
19
+ once it comes back from the application:
20
+
21
+ ```
22
+
23
+ ---------------------> request ----------------------->
24
+
25
+ Middleware 1 Middleware 2 Application
26
+
27
+ <--------------------- response <-----------------------
28
+
29
+
30
+ ```
31
+
32
+ `web_pipe` follows a simpler but equally powerful model of a one-way
33
+ pipe and abstracts it on top of rack. A struct that contains all the
34
+ data from a web request is piped trough a stack of operations which
35
+ take it as argument and return a new instance of it where response
36
+ data can be added at any step.
37
+
38
+ ```
39
+
40
+ Operation 1 Operation 2 Operation 3
41
+
42
+ --------------------- request/response ---------------->
43
+
44
+ ```
45
+
46
+ In addition to that, any operation in the stack has the power to stop
47
+ the propagation of the pipe, leaving any downstream operation
48
+ unexecuted. This is mainly useful to unauthorize a request while being
49
+ sure that nothing else will be done to the response.
50
+
51
+ As you may know, this is the same model used by Elixir's
52
+ [`plug`](https://hexdocs.pm/plug/readme.html), from which `web_pipe`
53
+ takes inspiration.
54
+
55
+ This library has been designed to work frictionless along the
56
+ [`dry-rb`]( https://dry-rb.org/) ruby ecosystem and it uses some of
57
+ its libraries internally.
58
+
59
+ ## Usage
60
+
61
+ This is a sample `config.ru` for a contrived application built with
62
+ `web_pipe`. It simply fetches a user from an `id` request
63
+ parameter. If the user is not found, it returns a not found
64
+ response. If it is found, it will unauthorize when it is a non `admin`
65
+ user or greet it otherwise:
66
+
67
+ ```
68
+ rackup --port 4000
69
+ # http://localhost:4000?id=1 => Hello Alice
70
+ # http://localhost:4000?id=2 => Unauthorized
71
+ # http://localhost:4000?id=3 => Not found
72
+ ```
73
+
74
+ ```ruby
75
+ # config.ru
76
+ require "web_pipe"
77
+
78
+ UsersRepo = {
79
+ 1 => { name: 'Alice', admin: true },
80
+ 2 => { name: 'Joe', admin: false }
81
+ }
82
+
83
+ class GreetingAdminApp
84
+ include WebPipe
85
+
86
+ plug :set_content_type
87
+ plug :fetch_user
88
+ plug :authorize
89
+ plug :greet
90
+
91
+ private
92
+
93
+ def set_content_type(conn)
94
+ conn.add_response_header(
95
+ 'Content-Type', 'text/html'
96
+ )
97
+ end
98
+
99
+ def fetch_user(conn)
100
+ user = UsersRepo[conn.params['id'].to_i]
101
+ if user
102
+ conn.
103
+ put(:user, user)
104
+ else
105
+ conn.
106
+ set_status(404).
107
+ set_response_body('<h1>Not foud</h1>').
108
+ taint
109
+ end
110
+ end
111
+
112
+ def authorize(conn)
113
+ if conn.fetch(:user)[:admin]
114
+ conn
115
+ else
116
+ conn.
117
+ set_status(401).
118
+ set_response_body('<h1>Unauthorized</h1>').
119
+ taint
120
+ end
121
+ end
122
+
123
+ def greet(conn)
124
+ conn.
125
+ set_response_body("<h1>Hello #{conn.fetch(:user)[:name]}</h1>")
126
+ end
127
+ end
128
+
129
+ run GreetingAdminApp.new
130
+ ```
131
+
132
+ As you see, steps required are:
133
+
134
+ - Include `WebPipe` in a class.
135
+ - Specify the stack of operations with `plug`.
136
+ - Implement those operations.
137
+ - Initialize the class to obtain resulting rack application.
138
+
139
+ `WebPipe::Conn` is a struct of request and response date, seasoned
140
+ with methods that act on its data. These methods are designed to
141
+ return a new instance of the struct each time, so they encourage
142
+ immutability and make method chaining possible.
143
+
144
+ Each operation in the pipe must accept a single argument of a
145
+ `WebPipe::Conn` instance and it must also return an instance of it.
146
+ In fact, what the first operation in the pipe takes is a
147
+ `WebPipe::Conn::Clean` subclass instance. When one of your operations
148
+ calls `#taint` on it, a `WebPipe::Conn::Dirty` is returned and the pipe
149
+ is halted. This one or the 'clean' instance that reaches the end of
150
+ the pipe will be in command of the web response.
151
+
152
+ Operations have the chance to prepare data to be consumed by
153
+ downstream operations. Data can be added to the struct through
154
+ `#put(key, value)`, while it can be consumed with `#fetch(key)`.
155
+
156
+ Attributes and methods in `WebPipe::Conn` are [fully
157
+ documented](https://www.rubydoc.info/github/waiting-for-dev/web_pipe/master/WebPipe/Conn).
158
+
159
+ ### Specifying operations
160
+
161
+ There are several ways you can `plug` operations to the pipe:
162
+
163
+ #### Instance methods
164
+
165
+ Operations can be just methods defined in the pipe class. This is what
166
+ you saw in the previous example:
167
+
168
+ ```ruby
169
+ class App
170
+ include WebPipe
171
+
172
+ plug :hello
173
+
174
+ private
175
+
176
+ def hello(conn)
177
+ # ...
178
+ end
179
+ ```
180
+
181
+ #### Proc (or anything responding to `#call`)
182
+
183
+ Operations can also be defined inline, through the `with:` keyword, as
184
+ anything that responds to `#call`, like a `Proc`:
185
+
186
+ ```ruby
187
+ class App
188
+ include WebPipe
189
+
190
+ plug :hello, with: ->(conn) { conn }
191
+ end
192
+ ```
193
+
194
+ #### Container
195
+
196
+ When `with:` is a `String` or a `Symbol`, it can be used as the key to
197
+ resolve an operation from a container. A container is just anything
198
+ responding to `#[]`.
199
+
200
+ The container to be used is configured when you include `WebPipe`:
201
+
202
+ ```ruby
203
+ class App
204
+ Container = Hash[
205
+ 'plugs.hello' => ->(conn) { conn }
206
+ ]
207
+
208
+ include WebPipe.(container: Container)
209
+
210
+ plug :hello, with: 'plugs.hello'
211
+ end
212
+ ```
213
+
214
+ ### Operations injection
215
+
216
+ Operations can be injected when the application is initialized,
217
+ overriding those configured through `plug`:
218
+
219
+ ```ruby
220
+ class App
221
+ include WebPipe
222
+
223
+ plug :hello, with: ->(conn) { conn.set_response_body('Hello') }
224
+ end
225
+
226
+ run App.new(
227
+ hello: ->(conn) { conn.set_response_body('Injected') }
228
+ )
229
+ ```
230
+
231
+ In the previous example, resulting response body would be `Injected`.
232
+
233
+ ### Rack middlewares
234
+
235
+ Rack middlewares can be added to the generated application through
236
+ `use`. They will be executed in declaration order before the pipe of
237
+ plugs:
238
+
239
+ ```ruby
240
+ class App
241
+ include WebPipe
242
+
243
+ use Middleware1
244
+ use Middleware2, option_1: value_1
245
+
246
+ plug :hello, with: ->(conn) { conn }
247
+ end
248
+ ```
249
+
250
+ ### Standalone usage
251
+
252
+ If you prefer, you can use the application builder without the
253
+ DSL. For that, you just have to initialize a `WebPipe::App` with an
254
+ array of all the operations to be performed:
255
+
256
+ ```ruby
257
+ require 'web_pipe/app`
258
+
259
+ op_1 = ->(conn) { conn.set_status(200) }
260
+ op_2 = ->(conn) { conn.set_response_body('Hello') }
261
+
262
+ WebPipe::App.new([op_1, op_2])
263
+ ```
264
+
265
+ ## Current status
266
+
267
+ `web_pipe` is in active development. The very basic features to build
268
+ a rack application are all available. However, very necessary
269
+ conveniences to build a production application, for example a session
270
+ mechanism, are still missing.
271
+
272
+ ## Contributing
273
+
274
+ Bug reports and pull requests are welcome on GitHub at
275
+ https://github.com/waiting-for-dev/web_pipe.
276
+
277
+ ## Release Policy
278
+
279
+ `web_pipe` follows the principles of [semantic versioning](http://semver.org/).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "web_pipe"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "pry"
14
+ Pry.start