mandrake 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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bbdd8197b3ad464e48233e2d45f606524ddb7173
4
+ data.tar.gz: 13d1da46f40a86180e7fe7e06d12184dfef4fd4b
5
+ SHA512:
6
+ metadata.gz: d5be45a91f9ba3ce2834bb4613ea0674c2eac952be45aacb6e5471545dabcabb762497627c453f8423c683992276f46eea073061d13d2c5345a871f66f4fa6c9
7
+ data.tar.gz: b83f14dba4208000e3b0bc8904163eb5c1e217c2df88867d3eebea0f9987c7420ff4a521955546a3d0f77f0020dfda10635d67d543f5f750604db9680b4d8ea3
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mandrake.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 namusyaka
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,82 @@
1
+ # Mandrake
2
+
3
+ Mandrake loads middlewares conditionally, and it provides two options and DSL for setting conditions.
4
+ If you use Mandrake, you can avoid executing unnecessary middlewares by setting conditions.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'mandrake'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install mandrake
19
+
20
+ ## Builder
21
+
22
+ Mandrake provides `:if` and `:unless` options which are available on `#use` method.
23
+ These options can be used to set the condition to enable the middleware at runtime.
24
+
25
+ ```ruby
26
+ Mandrake::Builder.new do
27
+ use Rack::Deflate, if: request.path_info.start_with?("/deflater")
28
+ use Rack::ETag, unless: request.path_info.end_with?("etag")
29
+ run ->(env){ [200, {'Content-Type' => 'text/plain'}, ["Hello World"]] }
30
+ end
31
+ ```
32
+
33
+ Mandrake provides `env` and `request` methods for building the conditional expression.
34
+ They can be used in the block.
35
+
36
+ ```ruby
37
+ Mandrake::Builder.new do
38
+ request.path_info.start_with?("/public") #=> 'request.path_info.start_with?("/public")'
39
+ env["PATH_INFO"] == "/public" #=> 'env["PATH_INFO"] == "/public"'
40
+ end
41
+ ```
42
+
43
+ If you want to use these without the block executed on initialization,
44
+ you must pass the **proc** to the conditional options.
45
+
46
+ ```ruby
47
+ builder = Mandrake::Builder.new
48
+ builder.use Rack::Deflater, if: proc{ request.path_info.start_with?("/public") }
49
+ ```
50
+
51
+ The arguments of these options allow to pass the **lambda**, but its behavior is different from **proc**.
52
+ **lambda** is defined as the validation method by `define_method`. Therefore the validation method will be slow.
53
+
54
+ ```ruby
55
+ Mandrake::Builder.new do
56
+ use Rack::Deflate, if: proc{ request.path_info.start_with?("/deflater") }
57
+ run ->(env){ [200, {'Content-Type' => 'text/plain'}, ["Hello World"]] }
58
+ end
59
+ ```
60
+
61
+ In the end, `Mandrake::Builder` inherits `Rack::Builder`, so you can use this like `Rack::Builder` basically.
62
+ Of course, it does not break the compatibility.
63
+
64
+ ## Middleware
65
+
66
+ This class is for incorporating mandrake into your application easily.
67
+ You can use as well as the Rack Middleware.
68
+
69
+ ```ruby
70
+ use Mandrake::Middleware do
71
+ use Rack::Deflate, if: request.path_info.start_with?("/deflater")
72
+ use Rack::ETag, unless: request.path_info.end_with?("etag")
73
+ end
74
+ ```
75
+
76
+ ## Contributing
77
+
78
+ 1. Fork it ( https://github.com/namusyaka/mandrake/fork )
79
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
80
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
81
+ 4. Push to the branch (`git push origin my-new-feature`)
82
+ 5. Create a new Pull Request
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+ desc "Run all specs."
5
+ RSpec::Core::RakeTask.new(:spec) do |spec|
6
+ spec.pattern = 'spec/**/*_spec.rb'
7
+ spec.rspec_opts = "--color"
8
+ end
9
+ task default: :spec
@@ -0,0 +1,7 @@
1
+ require "mandrake/version"
2
+ require 'mandrake/builder'
3
+
4
+ module Mandrake
5
+ autoload :Builder, "mandrake/builder"
6
+ autoload :Middleware, "mandrake/middleware"
7
+ end
@@ -0,0 +1,51 @@
1
+ require 'mandrake/translator'
2
+ require 'rack'
3
+ require 'forwardable'
4
+
5
+ module Mandrake
6
+ # Mandrake::Builder based on Rack::Builder
7
+ # It adds :if and :unless options to the `use` method,
8
+ # and enables conditional expression builders such as `env` and `request`
9
+ # @example
10
+ # Mandrake::Builder.new do
11
+ # use Rack::Deflater, if: request.path_info.start_with?("/deflater")
12
+ # use Rack::ETag, unless: request.path_info.end_with?("etag")
13
+ # run ->(env){ [200, {'Content-Type' => 'text/plain'}, ["Hello World"]] }
14
+ # end
15
+ class Builder < Rack::Builder
16
+ extend Forwardable
17
+ def_delegators :translator, :use, :request, :env
18
+
19
+ # Converts to application by using Mandrake::Translator
20
+ def to_app
21
+ app = @map ? generate_map(@run, @map) : @run
22
+ fail "missing run or map statement" unless app
23
+ app = translator.translate.reverse.inject(app) do |application, middleware|
24
+ middleware.call(application)
25
+ end
26
+ @warmup.call(app) if @warmup
27
+ app
28
+ end
29
+
30
+ # This method is for delegating methods
31
+ # Returns an instance of Mandrake::Translator for use in delegation and to_app
32
+ # @return [Mandrake::Translator]
33
+ def translator
34
+ @translator ||= Translator.new
35
+ end
36
+
37
+ private :translator
38
+
39
+ if Rack.release <= "1.5"
40
+ def warmup(prc=nil, &block)
41
+ @warmup = prc || block
42
+ end
43
+
44
+ def generate_map(default_app, mapping)
45
+ mapped = default_app ? {'/' => default_app} : {}
46
+ mapping.each { |r,b| mapped[r] = self.class.new(default_app, &b).to_app }
47
+ Rack::URLMap.new(mapped)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,11 @@
1
+ require 'mandrake/dsl/stringify'
2
+ require 'mandrake/dsl/conditional_expression'
3
+
4
+ module Mandrake
5
+ # A module as namespace for defining DSL classes
6
+ # @!visibility private
7
+ module DSL
8
+ Env = Class.new(Stringify)
9
+ Request = Class.new(Stringify)
10
+ end
11
+ end
@@ -0,0 +1,62 @@
1
+ module Mandrake
2
+ module DSL
3
+ # A class for building conditional expressions
4
+ # @example
5
+ # cond1 = Mandrake::DSL::If.new('env["PATH_INFO"] == "/"')
6
+ # cond2 = Mandrake::DSL::Unless.new('env["REQUEST_METHOD"] == "GET"')
7
+ # cond1 + cond2 #=> '(env["PATH_INFO"] == "/") && !(env["REQUEST_METHOD"] == "GET")'
8
+ # cond1.concat(cond2) #=> '(env["PATH_INFO"] == "/") && !(env["REQUEST_METHOD"] == "GET")'
9
+ # cond1 #=> '(env["PATH_INFO"] == "/") && !(env["REQUEST_METHOD"] == "GET")'
10
+ # @!visibility private
11
+ class ConditionalExpression
12
+ attr_accessor :code
13
+
14
+ # @!visibility private
15
+ def self.expression
16
+ @expression
17
+ end
18
+
19
+ # @param [String] text
20
+ # @!visibility private
21
+ def self.template(text)
22
+ @expression ||= text
23
+ end
24
+
25
+ # @param [Mandrake::DSL::Stringify::Relation, Proc] code
26
+ # @!visibility private
27
+ def initialize(code)
28
+ @code = code
29
+ end
30
+
31
+ # @param [Mandrake::DSL::ConditonalExpression] other_expression
32
+ # @!visibility private
33
+ def +(other_expression)
34
+ expression + " && " + other_expression.to_s
35
+ end
36
+
37
+ # Concatenates an other expression with self
38
+ # @param [Mandrake::DSL::ConditonalExpression] other_expression
39
+ # @!visibility private
40
+ def concat(other_expression)
41
+ @expression = send(:+, other_expression)
42
+ end
43
+
44
+ # @!visibility private
45
+ def expression
46
+ @expression ||= self.class.expression.gsub(/{:condition:}/, @code.to_s)
47
+ end
48
+
49
+ alias to_s expression
50
+ end
51
+
52
+ # @!visibility private
53
+ class If < ConditionalExpression
54
+ template "({:condition:})"
55
+ end
56
+
57
+ # @!visibility private
58
+ class Unless < ConditionalExpression
59
+ template "!({:condition:})"
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,58 @@
1
+ module Mandrake
2
+ module DSL
3
+ # A class for building an expression by using the method_missing method
4
+ # @example
5
+ # Oh = Class.new(Mandrake::DSL::Stringify)
6
+ # Oh.stringify.Mikey.hello('boy').to_s #=> "oh.Mikey.hello(\"boy\")"
7
+ # Oh.stringify.James.goodbye('man').to_s #=> "oh.James.goodbye(\"man\")"
8
+ # Oh.stringify.Mikey.hello('boy').and(Oh.stringify.James.goodbye('man')).to_s
9
+ # #=> "oh.Mikey.hello(\"boy\") && oh.James.goodbye(\"man\")"
10
+ # @!visibility private
11
+ class Stringify
12
+ # @!visibility private
13
+ def stringify
14
+ self.class.stringify
15
+ end
16
+
17
+ # @!visibility private
18
+ def self.stringify
19
+ Stringify::Relation.new(self)
20
+ end
21
+
22
+ # @!visibility private
23
+ class Relation
24
+ # Defines a method for the relation
25
+ # @param [String, Symbol] name
26
+ # @yield block on which to base the method
27
+ # @!visibility private
28
+ def self.chain(name, &block)
29
+ define_method(name){|*args| instance_exec(*args, &block); self; }
30
+ end
31
+
32
+ # @param [Class] klass
33
+ # @!visibility private
34
+ def initialize(klass)
35
+ @class = klass
36
+ reset_string
37
+ end
38
+
39
+ # Returns a string built by this class
40
+ # @!visibility private
41
+ def to_s
42
+ @string
43
+ end
44
+
45
+ chain(:[]) {|*args| @string << "[#{args.map(&:inspect) * ", "}]" }
46
+ chain(:==) {|value| @string << " == #{value.inspect}" }
47
+ chain(:and) {|relation| @string << " && " << relation.to_s }
48
+ chain(:or) {|relation| @string << " || " << relation.to_s }
49
+ chain(:method_missing) {|method_name, *args|
50
+ @string << ".#{method_name}"
51
+ @string << "(#{args.map(&:inspect) * ", "})" unless args.length.zero? }
52
+ chain(:reset_string) { @string = "#{@class.name.downcase.split(/::/).last}" }
53
+ alias equal ==
54
+ alias eq equal
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,17 @@
1
+ require 'mandrake/builder'
2
+
3
+ module Mandrake
4
+ # Mandrake::Middleware can quickly be used on your application
5
+ # @example
6
+ # use Mandrake::Middleware do
7
+ # use Rack::ETag, if: request.path_info.start_with?("/public")
8
+ # end
9
+ module Middleware
10
+ def self.new(app, &block)
11
+ builder = Mandrake::Builder.new
12
+ builder.instance_eval(&block)
13
+ builder.run(app)
14
+ builder.to_app
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,114 @@
1
+ require 'mandrake/dsl'
2
+
3
+ module Mandrake
4
+ # A class for being delegated from the Mandrake::Builder class
5
+ # @!visibility private
6
+ class Translator
7
+ # Construcsts an instance of Mandrake::Translator
8
+ def initialize
9
+ @middlewares = []
10
+ end
11
+
12
+ # @overload Rack::Builder#use(middleware, *args, options = {})
13
+ # @param [Hash] options The options can be included :if and :unless keys
14
+ # @return [Array] All registered middlewares
15
+ def use(middleware, *args, **options, &block)
16
+ parameters = extract_parameters_from(options)
17
+ @middlewares << Any.new(middleware, (options.empty? ? args : (args << options)), parameters, block)
18
+ end
19
+
20
+ # Translates middlewares to compatible data with Rack::Builder's processing
21
+ # @return [Array]
22
+ def translate
23
+ @middlewares.map do |middleware|
24
+ if middleware.conditional?
25
+ then proc{|app| Wrapper.new(app, middleware.construct(app), middleware.condition) }
26
+ else proc{|app| middleware.construct(app) }
27
+ end
28
+ end
29
+ end
30
+
31
+ # The string builder for building an expression
32
+ # @see Mandrake::DSL::Env
33
+ def env
34
+ DSL::Env.stringify
35
+ end
36
+
37
+ # The string builder for building an expression
38
+ # @see Mandrake::DSL::Request
39
+ def request
40
+ DSL::Request.stringify
41
+ end
42
+
43
+ # Extracts :if and :unless pairs from options
44
+ # @!visibility private
45
+ def extract_parameters_from(options)
46
+ [:if, :unless].inject({}) do |parameters, key|
47
+ expression = parameters[key] = options.delete(key) if options[key]
48
+ if expression.instance_of?(Proc) && !expression.lambda?
49
+ parameters[key] = instance_eval(&expression)
50
+ end
51
+ parameters
52
+ end
53
+ end
54
+
55
+ private :extract_parameters_from
56
+
57
+ # A class for use in Mandrake::Translator#translate
58
+ # @!visibility private
59
+ class Any < Struct.new(:klass, :arguments, :parameters, :block)
60
+ # @!visibility private
61
+ def condition
62
+ @condition ||=
63
+ begin
64
+ expressions = []
65
+ expressions << DSL::If.new(parameters[:if]) if parameters[:if]
66
+ expressions << DSL::Unless.new(parameters[:unless]) if parameters[:unless]
67
+ expressions.inject(:+)
68
+ end
69
+ end
70
+
71
+ # @!visibility private
72
+ def construct(app)
73
+ klass.new(app, *arguments, &block)
74
+ end
75
+
76
+ # @!visibility private
77
+ def conditional?
78
+ !!condition
79
+ end
80
+ end
81
+
82
+ # A wrapper class for conditional middleware which is containing the validator
83
+ # @!visibility private
84
+ class Wrapper
85
+ attr_accessor :env, :request
86
+
87
+ # @!visibility private
88
+ def initialize(app, middleware, condition)
89
+ @app = app
90
+ @middleware = middleware
91
+ add_validator(condition)
92
+ end
93
+
94
+ # @param [Hash] env
95
+ # @!visibility private
96
+ def call(env)
97
+ @env = env
98
+ @request = Rack::Request.new(@env)
99
+ valid? ? @middleware.call(env) : @app.call(env)
100
+ end
101
+
102
+ # @param [String, Proc] condition
103
+ # @!visibility private
104
+ def add_validator(condition)
105
+ code = condition.code
106
+ if code.instance_of?(Proc) && code.lambda?
107
+ singleton_class.send(:define_method, :valid?, &code)
108
+ else
109
+ instance_eval("def valid?; #{condition} end")
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,3 @@
1
+ module Mandrake
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mandrake/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "mandrake"
8
+ spec.version = Mandrake::VERSION
9
+ spec.authors = ["namusyaka"]
10
+ spec.email = ["namusyaka@gmail.com"]
11
+ spec.summary = %q{Mandrake loads middlewares conditionally, and it provides two options and DSL for setting conditions}
12
+ spec.description = spec.summary
13
+ spec.homepage = "https://github.com/namusyaka/mandrake"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "rack"
22
+ spec.add_development_dependency "bundler", "~> 1.6"
23
+ spec.add_development_dependency "rake"
24
+ spec.add_development_dependency "rspec"
25
+ end
@@ -0,0 +1,5 @@
1
+ class Anything
2
+ def self.call(env)
3
+ [200, {'Content-Type' => 'text/plain'}, ['OK']]
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ =begin
2
+
3
+ =end
4
+ run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['OK']] }
@@ -0,0 +1,5 @@
1
+ run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['OK']] }
2
+ __END__
3
+ Should not be evaluated
4
+ Neither should
5
+ This
@@ -0,0 +1 @@
1
+ run lambda{ |env| [200, {'Content-Type' => 'text/plain'}, [__LINE__.to_s]] }
@@ -0,0 +1,2 @@
1
+ #\ -d
2
+ run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['OK']] }
@@ -0,0 +1,257 @@
1
+ require 'spec_helper'
2
+ require 'rack/builder'
3
+ require 'rack/lint'
4
+ require 'rack/mock'
5
+ require 'rack/showexceptions'
6
+ require 'rack/urlmap'
7
+
8
+ describe Mandrake::Builder do
9
+ it "supports mapping" do
10
+ app = builder_to_app do
11
+ map '/' do |outer_env|
12
+ run lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, ['root']] }
13
+ end
14
+ map '/sub' do
15
+ run lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, ['sub']] }
16
+ end
17
+ end
18
+ expect(Rack::MockRequest.new(app).get("/").body.to_s).to eq 'root'
19
+ expect(Rack::MockRequest.new(app).get("/sub").body.to_s).to eq 'sub'
20
+ end
21
+
22
+ it "doesn't dupe env even when mapping" do
23
+ app = builder_to_app do
24
+ use NothingMiddleware
25
+ map '/' do |outer_env|
26
+ run lambda { |inner_env|
27
+ inner_env['new_key'] = 'new_value'
28
+ [200, {"Content-Type" => "text/plain"}, ['root']]
29
+ }
30
+ end
31
+ end
32
+ expect(Rack::MockRequest.new(app).get("/").body.to_s).to eq 'root'
33
+ expect(NothingMiddleware.env['new_key']).to eq 'new_value'
34
+ end
35
+
36
+ it "chains apps by default" do
37
+ app = builder_to_app do
38
+ use Rack::ShowExceptions
39
+ run lambda { |env| raise "bzzzt" }
40
+ end
41
+
42
+ expect(Rack::MockRequest.new(app).get("/")).to be_server_error
43
+ expect(Rack::MockRequest.new(app).get("/")).to be_server_error
44
+ expect(Rack::MockRequest.new(app).get("/")).to be_server_error
45
+ end
46
+
47
+ it "has implicit #to_app" do
48
+ app = builder do
49
+ use Rack::ShowExceptions
50
+ run lambda { |env| raise "bzzzt" }
51
+ end
52
+
53
+ expect(Rack::MockRequest.new(app).get("/")).to be_server_error
54
+ expect(Rack::MockRequest.new(app).get("/")).to be_server_error
55
+ expect(Rack::MockRequest.new(app).get("/")).to be_server_error
56
+ end
57
+
58
+ it "supports blocks on use" do
59
+ app = builder do
60
+ use Rack::ShowExceptions
61
+ use Rack::Auth::Basic do |username, password|
62
+ 'secret' == password
63
+ end
64
+
65
+ run lambda { |env| [200, {"Content-Type" => "text/plain"}, ['Hi Boss']] }
66
+ end
67
+
68
+ response = Rack::MockRequest.new(app).get("/")
69
+ expect(response).to be_client_error
70
+ expect(response.status).to eq 401
71
+
72
+ # with auth...
73
+ response = Rack::MockRequest.new(app).get("/",
74
+ 'HTTP_AUTHORIZATION' => 'Basic ' + ["joe:secret"].pack("m*"))
75
+ expect(response.status).to eq 200
76
+ expect(response.body.to_s).to eq 'Hi Boss'
77
+ end
78
+
79
+ it "has explicit #to_app" do
80
+ app = builder do
81
+ use Rack::ShowExceptions
82
+ run lambda { |env| raise "bzzzt" }
83
+ end
84
+
85
+ expect(Rack::MockRequest.new(app).get("/")).to be_server_error
86
+ expect(Rack::MockRequest.new(app).get("/")).to be_server_error
87
+ expect(Rack::MockRequest.new(app).get("/")).to be_server_error
88
+ end
89
+
90
+ it "can mix map and run for endpoints" do
91
+ app = builder do
92
+ map '/sub' do
93
+ run lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, ['sub']] }
94
+ end
95
+ run lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, ['root']] }
96
+ end
97
+
98
+ expect(Rack::MockRequest.new(app).get("/").body.to_s).to eq 'root'
99
+ expect(Rack::MockRequest.new(app).get("/sub").body.to_s).to eq 'sub'
100
+ end
101
+
102
+ it "accepts middleware-only map blocks" do
103
+ app = builder do
104
+ map('/foo') { use Rack::ShowExceptions }
105
+ run lambda { |env| raise "bzzzt" }
106
+ end
107
+
108
+ expect { Rack::MockRequest.new(app).get("/") }.to raise_error(RuntimeError)
109
+ expect(Rack::MockRequest.new(app).get("/foo")).to be_server_error
110
+ end
111
+
112
+ it "yields the generated app to a block for warmup" do
113
+ warmed_up_app = nil
114
+
115
+ app = Mandrake::Builder.new do
116
+ warmup { |a| warmed_up_app = a }
117
+ run lambda { |env| [200, {}, []] }
118
+ end.to_app
119
+
120
+ expect(warmed_up_app).to equal app
121
+ end
122
+
123
+ it "initialize apps once" do
124
+ app = builder do
125
+ class AppClass
126
+ def initialize
127
+ @called = 0
128
+ end
129
+ def call(env)
130
+ raise "bzzzt" if @called > 0
131
+ @called += 1
132
+ [200, {'Content-Type' => 'text/plain'}, ['OK']]
133
+ end
134
+ end
135
+
136
+ use Rack::ShowExceptions
137
+ run AppClass.new
138
+ end
139
+
140
+ expect(Rack::MockRequest.new(app).get("/").status).to eq 200
141
+ expect(Rack::MockRequest.new(app).get("/")).to be_server_error
142
+ end
143
+
144
+ it "allows use after run" do
145
+ app = builder do
146
+ run lambda { |env| raise "bzzzt" }
147
+ use Rack::ShowExceptions
148
+ end
149
+
150
+ expect(Rack::MockRequest.new(app).get("/")).to be_server_error
151
+ expect(Rack::MockRequest.new(app).get("/")).to be_server_error
152
+ expect(Rack::MockRequest.new(app).get("/")).to be_server_error
153
+ end
154
+
155
+ it 'complains about a missing run' do
156
+ expect { Rack::Lint.new Mandrake::Builder.app { use Rack::ShowExceptions }}.to raise_error(RuntimeError)
157
+ end
158
+
159
+ describe "parse_file" do
160
+ def config_file(name)
161
+ File.join(File.dirname(__FILE__), 'builder', name)
162
+ end
163
+
164
+ it "parses commented options" do
165
+ app, options = Mandrake::Builder.parse_file config_file('options.ru')
166
+ expect(Rack::MockRequest.new(app).get("/").body.to_s).to eq 'OK'
167
+ end
168
+
169
+ it "removes __END__ before evaluating app" do
170
+ app, _ = Mandrake::Builder.parse_file config_file('end.ru')
171
+ expect(Rack::MockRequest.new(app).get("/").body.to_s).to eq 'OK'
172
+ end
173
+
174
+ it "supports multi-line comments" do
175
+ expect { Mandrake::Builder.parse_file config_file('comment.ru') }.not_to raise_error
176
+ end
177
+
178
+ it "requires anything not ending in .ru" do
179
+ $: << File.dirname(__FILE__)
180
+ app, * = Mandrake::Builder.parse_file 'builder/anything'
181
+ expect(Rack::MockRequest.new(app).get("/").body.to_s).to eq 'OK'
182
+ $:.pop
183
+ end
184
+
185
+ it "sets __LINE__ correctly" do
186
+ app, _ = Mandrake::Builder.parse_file config_file('line.ru')
187
+ expect(Rack::MockRequest.new(app).get("/").body.to_s).to eq '1'
188
+ end
189
+ end
190
+
191
+ describe 'new_from_string' do
192
+ it "builds a rack app from string" do
193
+ app, = Mandrake::Builder.new_from_string "run lambda{|env| [200, {'Content-Type' => 'text/plane'}, ['OK']] }"
194
+ expect(Rack::MockRequest.new(app).get("/").body.to_s).to eq 'OK'
195
+ end
196
+ end
197
+
198
+ describe 'conditional builder' do
199
+ it 'support request and env DSL on block' do
200
+ app = builder do
201
+ _env = env
202
+ run lambda { |env| [200, {"Content-Type" => "text/plain"}, [request.to_s << _env.to_s]] }
203
+ end
204
+ expect(Rack::MockRequest.new(app).get("/").body.to_s).to eq 'requestenv'
205
+ end
206
+
207
+ it 'support :if option on use' do
208
+ app = builder do
209
+ use Dummy, if: request.path_info.start_with?("/public")
210
+ run lambda { |env| [200, {"Content-Type" => "text/plain"}, ['Hi Boss']] }
211
+ end
212
+ expect(Rack::MockRequest.new(app).get("/").body.to_s).to eq 'Hi Boss'
213
+ expect(Rack::MockRequest.new(app).get("/public").body.to_s).to eq 'YAY!'
214
+ end
215
+
216
+ it 'support :unless option on use' do
217
+ app = builder do
218
+ use Dummy, unless: request.path_info.start_with?("/public")
219
+ run lambda { |env| [200, {"Content-Type" => "text/plain"}, ['Hi Boss']] }
220
+ end
221
+ expect(Rack::MockRequest.new(app).get("/").body.to_s).to eq 'YAY!'
222
+ expect(Rack::MockRequest.new(app).get("/public").body.to_s).to eq 'Hi Boss'
223
+ end
224
+
225
+ it 'allow to pass proc to :if and :unless options' do
226
+ app = builder do
227
+ use Dummy, if: proc{ request.path_info.start_with?("/public") }
228
+ run lambda { |env| [200, {"Content-Type" => "text/plain"}, ['Hi Boss']] }
229
+ end
230
+ expect(Rack::MockRequest.new(app).get("/").body.to_s).to eq 'Hi Boss'
231
+ expect(Rack::MockRequest.new(app).get("/public").body.to_s).to eq 'YAY!'
232
+ end
233
+
234
+ it 'allow to pass lambda to :if and :unless options' do
235
+ app = builder do
236
+ use Dummy, if: ->{ request.path_info.start_with?("/public") }
237
+ run lambda { |env| [200, {"Content-Type" => "text/plain"}, ['Hi Boss']] }
238
+ end
239
+ expect(Rack::MockRequest.new(app).get("/").body.to_s).to eq 'Hi Boss'
240
+ expect(Rack::MockRequest.new(app).get("/public").body.to_s).to eq 'YAY!'
241
+ end
242
+
243
+ it 'can be set the arguments to #use' do
244
+ app = builder do
245
+ use OptionalMiddleware, hey: true do
246
+ "hey"
247
+ end
248
+ use Dummy, if: env["REQUEST_METHOD"] == "POST"
249
+ run lambda { |env| [200, {"Content-Type" => "text/plain"}, ['Hi Boss']] }
250
+ end
251
+ response = Rack::MockRequest.new(app).get("/public")
252
+ expect(response.headers["Options"]).to be_an_instance_of(String)
253
+ expect(response.headers["Block"]).to be_an_instance_of(String)
254
+ expect(response.body.to_s).to eq 'Hi Boss'
255
+ end
256
+ end
257
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mandrake::DSL::ConditionalExpression do
4
+ SampleCondition = Class.new(described_class)
5
+ SampleCondition.template '({:condition:})'
6
+
7
+ describe "+" do
8
+ let(:a){ Mandrake::DSL::If.new("a and b") }
9
+ let(:b){ Mandrake::DSL::Unless.new("a and b") }
10
+ subject { (a + b).to_s }
11
+ it { should eq('(a and b) && !(a and b)') }
12
+ end
13
+
14
+ describe "concat" do
15
+ let(:a){ Mandrake::DSL::If.new("a and b") }
16
+ let(:b){ Mandrake::DSL::Unless.new("a and b") }
17
+ before { a.concat(b) }
18
+ subject { a.to_s }
19
+ it { should eq('(a and b) && !(a and b)') }
20
+ end
21
+
22
+ describe "built-in classes" do
23
+ describe "If" do
24
+ let(:condition){ Mandrake::DSL::If.new("a and b") }
25
+ subject { condition.to_s }
26
+ it { should eq('(a and b)') }
27
+ end
28
+
29
+ describe "Unless" do
30
+ let(:condition){ Mandrake::DSL::Unless.new("a and b") }
31
+ subject { condition.to_s }
32
+ it { should eq('!(a and b)') }
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mandrake::DSL::Stringify do
4
+ Sample = Class.new(described_class)
5
+ context "basic" do
6
+ subject { Sample.stringify.unknown_method.eq('hello').to_s }
7
+ it { should eq('sample.unknown_method == "hello"') }
8
+ end
9
+
10
+ describe "arguments" do
11
+ context "with Fixnum" do
12
+ subject { Sample.stringify.hello_world(1234).eq('1234').to_s }
13
+ it { should eq('sample.hello_world(1234) == "1234"') }
14
+ end
15
+
16
+ context "with String" do
17
+ subject { Sample.stringify.hello_world('1234').eq('1234').to_s }
18
+ it { should eq('sample.hello_world("1234") == "1234"') }
19
+ end
20
+
21
+ context "with Array" do
22
+ subject { Sample.stringify.hello_world([1, 2, 3, 4]).eq([1, 2, 3, 4]).to_s }
23
+ it { should eq('sample.hello_world([1, 2, 3, 4]) == [1, 2, 3, 4]') }
24
+ end
25
+
26
+ context "with Hash" do
27
+ subject { Sample.stringify.hello_world({a: :hey}).eq({a: :hey}).to_s }
28
+ it { should eq('sample.hello_world({:a=>:hey}) == {:a=>:hey}') }
29
+ end
30
+ end
31
+
32
+
33
+ describe "[]" do
34
+ subject { Sample.stringify.method_that_does_not_exist['Hello'].eq('Hello World').to_s }
35
+ it { should eq('sample.method_that_does_not_exist["Hello"] == "Hello World"') }
36
+ end
37
+
38
+ describe "and" do
39
+ subject { Sample.stringify.a.eq('Hello World').and(Sample.stringify.b.start_with?("/public")).to_s }
40
+ it { should eq('sample.a == "Hello World" && sample.b.start_with?("/public")') }
41
+ end
42
+
43
+ describe "or" do
44
+ subject { Sample.stringify.a.eq('Hello World').or(Sample.stringify.b.start_with?("/public")).to_s }
45
+ it { should eq('sample.a == "Hello World" || sample.b.start_with?("/public")') }
46
+ end
47
+
48
+ context "use == instead of eq" do
49
+ subject { Sample.stringify.a == 'hello'}
50
+ it { should eq('sample.a == "hello"') }
51
+ end
52
+
53
+ context "without eq" do
54
+ subject { Sample.stringify.method_that_does_not_exist['Hello'].include?('howl').to_s }
55
+ it { should eq('sample.method_that_does_not_exist["Hello"].include?("howl")') }
56
+ end
57
+
58
+ describe "#reset_string" do
59
+ subject { Sample.stringify.method_that_does_not_exist.reset_string.hey['hello'].include?('howl').to_s }
60
+ it { should eq('sample.hey["hello"].include?("howl")') }
61
+ end
62
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mandrake::DSL do
4
+ shared_examples_for('a constant for dsl'){|constant|
5
+ it { expect(constant).to respond_to(:stringify) }}
6
+ it_behaves_like 'a constant for dsl', described_class::Env
7
+ it_behaves_like 'a constant for dsl', described_class::Request
8
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mandrake::Middleware do
4
+ it "can be executed in Rack::Builder" do
5
+ app = rack_builder do
6
+ use Mandrake::Middleware do
7
+ use Dummy, if: request.path_info.start_with?("/public")
8
+ end
9
+ run lambda { |env| [200, {"Content-Type" => "text/plain"}, ['Hi Boss']] }
10
+ end
11
+ expect(Rack::MockRequest.new(app).get("/").body.to_s).to eq 'Hi Boss'
12
+ expect(Rack::MockRequest.new(app).get("/public").body.to_s).to eq 'YAY!'
13
+ end
14
+ end
@@ -0,0 +1,63 @@
1
+ require File.expand_path("../../lib/mandrake", __FILE__)
2
+ require 'bundler' unless defined?(Bundler)
3
+ Bundler.require(:test)
4
+
5
+ class OptionalMiddleware
6
+ def initialize(app, options = {}, &block)
7
+ @app = app
8
+ @options = options
9
+ @block = true if block_given?
10
+ end
11
+
12
+ def call(env)
13
+ response = @app.call(env)
14
+ response[1].merge!("Options" => @options.inspect, "Block" => @block.inspect)
15
+ response
16
+ end
17
+ end
18
+
19
+ class NothingMiddleware
20
+ def initialize(app)
21
+ @app = app
22
+ end
23
+ def call(env)
24
+ @@env = env
25
+ response = @app.call(env)
26
+ response
27
+ end
28
+ def self.env
29
+ @@env
30
+ end
31
+ end
32
+
33
+ class Dummy
34
+ def initialize(app)
35
+ @app = app
36
+ end
37
+
38
+ def call(env)
39
+ [200, {}, ["YAY!"]]
40
+ end
41
+ end
42
+
43
+ def app
44
+ @app
45
+ end
46
+
47
+ def rack_builder(&block)
48
+ Rack::Lint.new Rack::Builder.new(&block)
49
+ end
50
+
51
+ def builder(&block)
52
+ Rack::Lint.new Mandrake::Builder.new(&block)
53
+ end
54
+
55
+ def builder_to_app(&block)
56
+ Rack::Lint.new Mandrake::Builder.new(&block).to_app
57
+ end
58
+
59
+ class TestApp
60
+ def call(env)
61
+ [200, {"Content-Type" => "text/plain"}, ["test app"]]
62
+ end
63
+ end
metadata ADDED
@@ -0,0 +1,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mandrake
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - namusyaka
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-08-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.6'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
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: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Mandrake loads middlewares conditionally, and it provides two options
70
+ and DSL for setting conditions
71
+ email:
72
+ - namusyaka@gmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - .gitignore
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - lib/mandrake.rb
83
+ - lib/mandrake/builder.rb
84
+ - lib/mandrake/dsl.rb
85
+ - lib/mandrake/dsl/conditional_expression.rb
86
+ - lib/mandrake/dsl/stringify.rb
87
+ - lib/mandrake/middleware.rb
88
+ - lib/mandrake/translator.rb
89
+ - lib/mandrake/version.rb
90
+ - mandrake.gemspec
91
+ - spec/builder/anything.rb
92
+ - spec/builder/comment.ru
93
+ - spec/builder/end.ru
94
+ - spec/builder/line.ru
95
+ - spec/builder/options.ru
96
+ - spec/builder_spec.rb
97
+ - spec/dsl/conditional_expression_spec.rb
98
+ - spec/dsl/stringify_spec.rb
99
+ - spec/dsl_spec.rb
100
+ - spec/middleware_spec.rb
101
+ - spec/spec_helper.rb
102
+ homepage: https://github.com/namusyaka/mandrake
103
+ licenses:
104
+ - MIT
105
+ metadata: {}
106
+ post_install_message:
107
+ rdoc_options: []
108
+ require_paths:
109
+ - lib
110
+ required_ruby_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - '>='
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ requirements: []
121
+ rubyforge_project:
122
+ rubygems_version: 2.0.14
123
+ signing_key:
124
+ specification_version: 4
125
+ summary: Mandrake loads middlewares conditionally, and it provides two options and
126
+ DSL for setting conditions
127
+ test_files:
128
+ - spec/builder/anything.rb
129
+ - spec/builder/comment.ru
130
+ - spec/builder/end.ru
131
+ - spec/builder/line.ru
132
+ - spec/builder/options.ru
133
+ - spec/builder_spec.rb
134
+ - spec/dsl/conditional_expression_spec.rb
135
+ - spec/dsl/stringify_spec.rb
136
+ - spec/dsl_spec.rb
137
+ - spec/middleware_spec.rb
138
+ - spec/spec_helper.rb
139
+ has_rdoc: