modware 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
+ SHA1:
3
+ metadata.gz: 89d92353fe1c300cb4eab370fa9838b6d104e8a6
4
+ data.tar.gz: 5d0f202e063fd925a3d57a663e7bcc54548f707b
5
+ SHA512:
6
+ metadata.gz: 24ac95fc4d539362292a322a92f9d7111671d7fd76fcc312f4b9494b27e26c1d113d110286eeb1c8899d835b02cafa61ca189c20ff2069d4b4d2a34aaae1fbd9
7
+ data.tar.gz: ca9b22dc6012180c1a109ae2713855d5866bc1969ba75b41a4141074b96ac6668f722084bf7d2d36595662668779b282481ccfe88b4d5621f99c418a1fc1fd18
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
@@ -0,0 +1,7 @@
1
+ sudo: false
2
+ rvm:
3
+ - 2.1.5
4
+ script: bundle exec rake spec_with_coveralls
5
+ notifications:
6
+ recipients:
7
+ - ronen@barzel.org
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in modware.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 ronen barzel
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,106 @@
1
+ [![Gem Version](https://badge.fury.io/rb/modware.svg)](http://badge.fury.io/rb/modware)
2
+ [![Build Status](https://secure.travis-ci.org/ronen/modware.svg)](http://travis-ci.org/ronen/modware)
3
+ [![Coverage Status](https://img.shields.io/coveralls/ronen/modware.svg)](https://coveralls.io/r/ronen/modware)
4
+
5
+ # Modware
6
+
7
+ Modware is a library for using middleware (pipeline) patterns in Ruby projects. It features a simple interface and supports "callback" style semantics in the middleware stack, including `before`, `after`, and `around` methods.
8
+
9
+
10
+ ## Installation
11
+
12
+ As usual:
13
+
14
+ ```ruby
15
+ gem 'modware' # in a Gemfile
16
+ spec.add_dependency 'modware' # in a .gemspec
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ### Creating a stack
22
+
23
+ Create a stack using:
24
+
25
+ ```ruby
26
+ stack = Modware::Stack.new(env: klass)
27
+ ```
28
+
29
+ where `klass` is a Class for the environment instance that will be passed to the layers of the stack. As a shorthand for the common case, you can simply pass an array of keys, e.g.
30
+
31
+ ```ruby
32
+ stack = Modware::Stack.new(env: [:name, :options, :results])
33
+ ```
34
+
35
+ and Modware will define a class that accepts those keys as keyword arguments, and has accessor methods for each (see
36
+ [`KeyStruct`](https://rubygems.org/gems/key_struct)).
37
+
38
+
39
+ ### Defining middleware
40
+
41
+ Middleware is defined as a module that defines one or more middleware methods:
42
+
43
+ ```ruby
44
+ module MyMiddleware
45
+
46
+ # define any of these as needed...
47
+
48
+ def before(env)
49
+ # code to be called before the base implementation
50
+ end
51
+
52
+ def after(env)
53
+ # code to be called after the base implementation
54
+ end
55
+
56
+ def around(env)
57
+ # setup/wrapper code
58
+ yield env # continues execution down the stack
59
+ # cleanup code
60
+ end
61
+
62
+ def implement(env)
63
+ # completely replaces the base implementation or any earlier middleware's implement()
64
+ end
65
+ end
66
+ ```
67
+
68
+ The module may use instance variables and define other methods as needed (e.g. to abide by [Metz' rule #2](http://robots.thoughtbot.com/sandi-metz-rules-for-developers)).
69
+
70
+ To add the middleware to a stack:
71
+
72
+ ```ruby
73
+ stack.add(MyMiddleware)
74
+ ```
75
+
76
+ Middleware is always added to the end of the stack.
77
+
78
+ ### Executing a stack
79
+
80
+ To execute a stack do:
81
+
82
+ ```ruby
83
+ stack.start(*args) { |env|
84
+ # base implementation
85
+ }
86
+ ```
87
+
88
+ The execution sequence of the stack is as follows:
89
+
90
+ 1. Create environment instance `env = env_klass.new(*args)`
91
+ 2. Call each middleware `before(env)` method, in the order they were added
92
+ 3. Call each middleware `around(env)` method, in the order they were added. This bottoms out with the last `implement(env)` method to be added, if any, otherwise the base implementation
93
+ 4. Call each middleware `after(env)` method, in the order they were added
94
+ 5. Return `env`
95
+
96
+ ### Helpers
97
+
98
+ * `Modware.is_middleware?(mod)` returns truthy if `mod`'s instance methods include any of the middleware methods `:before`, `:after`, `:around`, or `:implement`
99
+
100
+ ## See also
101
+
102
+ The [middleware](https://rubygems.org/gems/middleware) gem works well, following a [rack](http://rack.github.io/)-like execution model.
103
+
104
+ ## Contributing
105
+
106
+ Contributions welcome -- feel free to open issues or submit pull requests. Thanks!
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec) do |spec|
5
+ spec.rspec_opts = '-Ispec'
6
+ end
7
+
8
+ require 'coveralls/rake/task'
9
+ Coveralls::RakeTask.new
10
+ task :spec_with_coveralls => [:spec, 'coveralls:push']
@@ -0,0 +1,9 @@
1
+ require "modware/errors"
2
+ require "modware/stack"
3
+ require "modware/version"
4
+
5
+ module Modware
6
+ def self.is_middleware?(mod)
7
+ (mod.instance_methods & [:before, :around, :after, :implement]).any?
8
+ end
9
+ end
@@ -0,0 +1,4 @@
1
+ module Modware
2
+ class StackError < Exception
3
+ end
4
+ end
@@ -0,0 +1,80 @@
1
+ require 'its-it'
2
+ require 'key_struct'
3
+
4
+ module Modware
5
+ class Stack
6
+ def initialize(env:)
7
+ @env_klass = case env
8
+ when Class then env
9
+ else KeyStruct[*env]
10
+ end
11
+ @middlewares = []
12
+ end
13
+
14
+ def add(mod)
15
+ middleware = Middleware.new(self, mod)
16
+ @middlewares.last._next = middleware if @middlewares.any?
17
+ @middlewares << middleware
18
+ end
19
+
20
+ def start(*args, &implementation)
21
+ env = @env_klass.new(*args)
22
+ @base_implementation = implementation
23
+ execute_stack(env)
24
+ env
25
+ end
26
+
27
+ private
28
+
29
+ def execute_stack(env)
30
+ return call_implementation(env) unless @middlewares.any?
31
+
32
+ @middlewares.each do |middleware|
33
+ middleware.before env if middleware.respond_to? :before
34
+ end
35
+
36
+ @middlewares.first._call(env)
37
+
38
+ @middlewares.each do |middleware|
39
+ middleware.after env if middleware.respond_to? :after
40
+ end
41
+ end
42
+
43
+ def call_implementation(env)
44
+ if middleware = @middlewares.select(&it.respond_to?(:implement)).last
45
+ middleware.implement(env)
46
+ elsif @base_implementation
47
+ @base_implementation.call env
48
+ else
49
+ raise StackError, "No base implementation nor middleware implementation in stack"
50
+ end
51
+ end
52
+
53
+ class Middleware
54
+ attr_accessor :_next
55
+
56
+ def initialize(stack, mod)
57
+ @stack = stack
58
+ singleton_class.send :include, mod
59
+ end
60
+
61
+ def _call(env)
62
+ if respond_to? :around
63
+ around(env) { |env|
64
+ _continue env
65
+ }
66
+ else
67
+ _continue env
68
+ end
69
+ end
70
+
71
+ def _continue(env)
72
+ if self._next
73
+ self._next._call(env)
74
+ else
75
+ @stack.send :call_implementation, env
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,3 @@
1
+ module Modware
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'modware/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "modware"
8
+ spec.version = Modware::VERSION
9
+ spec.authors = ["ronen barzel"]
10
+ spec.email = ["ronen@barzel.org"]
11
+ spec.summary = %q{A middleware library, featuring a simple interface and "callback" style semantics in the middleware stack}
12
+ spec.homepage = "https://github.com/ronen/modware"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.required_ruby_version = ">= 2.1.0"
21
+ spec.add_dependency "key_struct", "~> 0.4"
22
+ spec.add_dependency "its-it"
23
+
24
+ spec.add_development_dependency 'coveralls'
25
+ spec.add_development_dependency "bundler", "~> 1.7"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_development_dependency "rspec", "~> 3.0"
28
+ spec.add_development_dependency "rspec-given"
29
+ spec.add_development_dependency "simplecov"
30
+ spec.add_development_dependency "simplecov-gem-profile"
31
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Env" do
4
+
5
+ When(:env) { Modware::Stack.new(env: klass).start(args) {} }
6
+
7
+ context "if env: klass is a Class" do
8
+
9
+ Given(:klass) { String }
10
+ Given(:args) { "EnvArgs" }
11
+
12
+ Then { expect(env).to eq klass.new(args) }
13
+
14
+ end
15
+
16
+ context "if env: klass is an array of keys" do
17
+
18
+ Given(:klass) { [:a, :b, :c] }
19
+ Given(:args) { {a: 1, b: 2, c: 3} }
20
+
21
+ Then { expect(env.to_hash).to eq args }
22
+ end
23
+
24
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Execution" do
4
+
5
+ Given(:stack) { Modware::Stack.new(env: [:result]) }
6
+
7
+ context "when start with no implementation" do
8
+
9
+ When(:env) { stack.start result: [] }
10
+
11
+ Then { expect(env).to have_failed(Modware::StackError, /implementation/) }
12
+ end
13
+
14
+ context "when start with base implementation" do
15
+
16
+ When(:env) { stack.start result: [] { |env| env.result << :base } }
17
+
18
+ Then { expect(env.result).to eq [:base] }
19
+
20
+ context "if add middleware1" do
21
+
22
+ Given { stack.add Factory.middleware(1, around: nil) }
23
+
24
+ Then { expect(env.result).to eq [:before1, :implement1, :after1 ] }
25
+
26
+ context "if add middleware2" do
27
+
28
+ Given { stack.add Factory.middleware(2, implement: nil) }
29
+
30
+ Then { expect(env.result).to eq [:before1, :before2, :around_pre2, :implement1, :around_post2, :after1, :after2 ] }
31
+ end
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Helper" do
4
+
5
+ let(:none) { { before: nil, after:nil, around: nil, implement: nil } }
6
+
7
+ context "Modware.is_middleware?" do
8
+ [:before, :after, :around, :implement].each do |method|
9
+ it "returns truthy for a module with #{method.inspect} method" do
10
+ expect(Modware.is_middleware? Factory.middleware(none.merge(method => true))).to be_truthy
11
+ end
12
+ end
13
+
14
+ it "returns falsey for other modules" do
15
+ expect(Modware.is_middleware? Factory.middleware(none.merge(other: true))).to be_falsey
16
+ end
17
+ end
18
+
19
+ end
@@ -0,0 +1,16 @@
1
+ require 'simplecov'
2
+ require 'simplecov-gem-profile'
3
+ SimpleCov.start "gem"
4
+
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
6
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
+
8
+ require 'rspec'
9
+ require 'rspec/given'
10
+ require 'modware'
11
+
12
+ Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each {|f| require f}
13
+
14
+ RSpec.configure do |config|
15
+ config.warnings = true
16
+ end
@@ -0,0 +1,40 @@
1
+ module Factory
2
+ def self.middleware(n=nil, before: true, after: true, around: true, implement: true, other: nil)
3
+
4
+ methods = []
5
+
6
+ methods << (before && <<-END)
7
+ def before(env)
8
+ env.result << :"before#{n}"
9
+ end
10
+ END
11
+
12
+ methods << (after && <<-END)
13
+ def after(env)
14
+ env.result << :"after#{n}"
15
+ end
16
+ END
17
+
18
+ methods << (around && <<-END)
19
+ def around(env)
20
+ env.result << :"around_pre#{n}"
21
+ yield env
22
+ env.result << :"around_post#{n}"
23
+ end
24
+ END
25
+
26
+ methods << (implement && <<-END)
27
+ def implement(env)
28
+ env.result << :"implement#{n}"
29
+ end
30
+ END
31
+
32
+ methods << (other && <<-END)
33
+ def other(env)
34
+ env.result << :"other#{n}"
35
+ end
36
+ END
37
+
38
+ Module.new.tap(&it.module_eval(methods.compact.join))
39
+ end
40
+ end
metadata ADDED
@@ -0,0 +1,192 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: modware
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - ronen barzel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: key_struct
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: its-it
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: coveralls
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.7'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.7'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec-given
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: simplecov-gem-profile
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description:
140
+ email:
141
+ - ronen@barzel.org
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - ".gitignore"
147
+ - ".travis.yml"
148
+ - Gemfile
149
+ - LICENSE.txt
150
+ - README.md
151
+ - Rakefile
152
+ - lib/modware.rb
153
+ - lib/modware/errors.rb
154
+ - lib/modware/stack.rb
155
+ - lib/modware/version.rb
156
+ - modware.gemspec
157
+ - spec/env_spec.rb
158
+ - spec/execution_spec.rb
159
+ - spec/helper_spec.rb
160
+ - spec/spec_helper.rb
161
+ - spec/support/factory.rb
162
+ homepage: https://github.com/ronen/modware
163
+ licenses:
164
+ - MIT
165
+ metadata: {}
166
+ post_install_message:
167
+ rdoc_options: []
168
+ require_paths:
169
+ - lib
170
+ required_ruby_version: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ version: 2.1.0
175
+ required_rubygems_version: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ requirements: []
181
+ rubyforge_project:
182
+ rubygems_version: 2.2.2
183
+ signing_key:
184
+ specification_version: 4
185
+ summary: A middleware library, featuring a simple interface and "callback" style semantics
186
+ in the middleware stack
187
+ test_files:
188
+ - spec/env_spec.rb
189
+ - spec/execution_spec.rb
190
+ - spec/helper_spec.rb
191
+ - spec/spec_helper.rb
192
+ - spec/support/factory.rb