holoserve 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2011 Skrill Holdings Ltd
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,131 @@
1
+
2
+ = HoloServe - Simple faking of HTTP APIs
3
+
4
+ This tool can be used to fake HTTP web APIs. It's meant to be used in a testing environment, to make the test suite
5
+ run faster and be independent from other API and network problems.
6
+
7
+ == Concept
8
+
9
+ HoloServe runs a rack application server, that matches any incoming request to a list of request profiles defined in
10
+ the server layout. If a match is found, the defined static response is returned. The name of the matched
11
+ request/response pair is saved in a request history. If no match is found, a 404 is returned and the
12
+ request data is stored in the bucket for unhandeled requests. These informations can be used to extend the server layout
13
+ with missing request handlers.
14
+
15
+ Layouts, history and bucket can be accessed via control routes, which are described below.
16
+
17
+ == Control routes
18
+
19
+ === POST /_control/layouts
20
+
21
+ It should receive a parameter named <tt>file</tt> that contains an uploaded YML file with the server layouts. See
22
+ {YML server layouts file format}[rdoc-label:YML-server-layouts-file-format] below.
23
+
24
+ === DELETE /_control/layouts
25
+
26
+ Removes all server layouts.
27
+
28
+ === GET /_control/layouts/ids
29
+
30
+ Returns the list of ids of all server layouts.
31
+
32
+ ==== Response example
33
+
34
+ [ "one", "two" ]
35
+
36
+ === PUT /_control/layouts/:id/current
37
+
38
+ Makes the layout with id <tt>:id</tt> the currently active one.
39
+
40
+ === GET /_control/layouts/current
41
+
42
+ Returns the id of the current layout.
43
+
44
+ ==== Response example
45
+
46
+ one
47
+
48
+ === GET /_control/bucket/requests
49
+
50
+ Returns a list of all requests that has been received, but couldn't be handled.
51
+
52
+ ==== Response example
53
+
54
+ [
55
+ {
56
+ "method": "POST",
57
+ "path": "test",
58
+ "headers": {
59
+ "REMOTE_ADDR": "127.0.0.1",
60
+ "REQUEST_METHOD": "GET",
61
+ "REQUEST_PATH": "/test",
62
+ "PATH_INFO": "/test",
63
+ "REQUEST_URI": "/test",
64
+ "SERVER_PROTOCOL": "HTTP/1.1",
65
+ "HTTP_VERSION": "HTTP/1.1",
66
+ "HTTP_ACCEPT": "*/*",
67
+ "HTTP_USER_AGENT": "Ruby",
68
+ "HTTP_HOST": "localhost:8080",
69
+ "SERVER_NAME": "localhost",
70
+ "SERVER_PORT": "8080",
71
+ "QUERY_STRING": "",
72
+ "SCRIPT_NAME": "",
73
+ "SERVER_SOFTWARE": "Unicorn 4.1.1"
74
+ }
75
+ }
76
+ ]
77
+
78
+ === GET /_control/history
79
+
80
+ Returns a list of all names of pairs that has been triggered.
81
+
82
+ ==== Response example
83
+
84
+ [ "create_received", "update_received" ]
85
+
86
+ === DELETE /_control/history
87
+
88
+ Removes all entries from the history.
89
+
90
+ == YML server layouts file format
91
+
92
+ The server layouts file should have the following format.
93
+
94
+ one:
95
+ -
96
+ name: "test_received"
97
+ request:
98
+ method: "POST"
99
+ path: "/test"
100
+ headers:
101
+ HTTP_USER_AGENT: "Ruby"
102
+ HTTP_AUTHORIZATION: "OAuth oauth_token=12345"
103
+ body:
104
+ "test=value"
105
+ parameters:
106
+ test: "value"
107
+ oauth:
108
+ oauth_token: "12345"
109
+ response:
110
+ status: 200
111
+ body:
112
+ "ok"
113
+ -
114
+ request:
115
+ method: "GET"
116
+ path: "/test"
117
+ response:
118
+ status: 200
119
+ body:
120
+ "ok too"
121
+
122
+ This example would define a server layout named <tt>one</tt> that has two request/response pairs. The first pair would
123
+ have the name <tt>test_received</tt> and would match a <tt>POST</tt> request to the path <tt>/test</tt>.
124
+
125
+ Notice, that the given request attributes are the _minimal_ values that have to match the incomong one. An incoming
126
+ request may has much more attributes (see bucket example above). If a request is matched, the corresponding pair name is
127
+ placed in the history.
128
+
129
+ As the sections <tt>headers</tt> and <tt>body</tt> are raw values from the request, the sections <tt>parameters</tt> and
130
+ <tt>oauth</tt> contain high-level values. The <tt>parameters</tt> hash is taken from the request body or the query and
131
+ the hash containing the OAuth values is parsed from a fitting <tt>HTTP_AUTHORIZATION</tt> header.
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ desc "Runs the unicorn interface server."
5
+ task :server do
6
+ system "bundle exec unicorn"
7
+ end
8
+
9
+ desc "Runs the shotgun interface server."
10
+ task :shotgun do
11
+ system "bundle exec shotgun"
12
+ end
13
+
14
+ begin
15
+ require 'cucumber'
16
+ require 'cucumber/rake/task'
17
+
18
+ Cucumber::Rake::Task.new(:features) do |task|
19
+ task.cucumber_opts = "features --format pretty"
20
+ end
21
+
22
+ namespace :features do
23
+ Cucumber::Rake::Task.new(:wip) do |task|
24
+ task.cucumber_opts = "features --format pretty --tags @wip"
25
+ end
26
+ end
27
+ rescue LoadError
28
+ end
29
+
30
+ begin
31
+ require 'rdoc'
32
+ require 'rdoc/task'
33
+
34
+ Rake::RDocTask.new do |rdoc|
35
+ rdoc.main = "README.rdoc"
36
+ rdoc.rdoc_files.include("README.rdoc", "lib/**/*.rb")
37
+ end
38
+ rescue LoadError
39
+ end
40
+
41
+ namespace :gem do
42
+
43
+ desc "Builds the gem"
44
+ task :build do
45
+ system "gem build *.gemspec && mkdir -p pkg/ && mv *.gem pkg/"
46
+ end
47
+
48
+ desc "Builds and installs the gem"
49
+ task :install => :build do
50
+ system "gem install pkg/"
51
+ end
52
+
53
+ end
data/bin/holoserve ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'bundler/setup'
5
+ require 'optparse'
6
+
7
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "lib", "holoserve"))
8
+
9
+ options = { }
10
+ OptionParser.new do |parser|
11
+ parser.banner = "Usage: holoserve [options]"
12
+ parser.separator ""
13
+ parser.separator "Options:"
14
+ parser.on("-p", "--port PORT", Integer, "The port holoserve should listen to.") do |value|
15
+ options[:port] = value
16
+ end
17
+ parser.on("-f", "--layouts-file FILE", "Load the specified layouts file on startup.") do |value|
18
+ options[:layouts_filename] = value
19
+ end
20
+ parser.on("-l", "--layout LAYOUT", "Selects the specified layout as the current one.") do |value|
21
+ options[:layout] = value
22
+ end
23
+ parser.on_tail("-h", "--help", "Shows the help message.") do
24
+ puts parser
25
+ exit
26
+ end
27
+ end.parse!(ARGV)
28
+
29
+ runner = Holoserve::Runner.new options
30
+ runner.run
@@ -0,0 +1,10 @@
1
+
2
+ class Holoserve::Bucket
3
+
4
+ attr_reader :requests
5
+
6
+ def initialize
7
+ @requests = [ ]
8
+ end
9
+
10
+ end
@@ -0,0 +1,48 @@
1
+ require 'yaml'
2
+
3
+ class Holoserve::Configuration
4
+
5
+ attr_reader :logger
6
+
7
+ attr_reader :layouts
8
+ attr_reader :layout_id
9
+
10
+ def initialize(logger)
11
+ @logger = logger
12
+ end
13
+
14
+ def layouts=(hash)
15
+ @layouts = Holoserve::Tool::Hash::KeySymbolizer.new(hash).hash
16
+ end
17
+
18
+ def layout_id=(value)
19
+ @layout_id = value.to_sym
20
+ logger.info "made '#{value}' the current layout"
21
+ end
22
+
23
+ def clear_layouts!
24
+ self.layouts = nil
25
+ end
26
+
27
+ def layout_ids
28
+ self.layouts ? self.layouts.keys : [ ]
29
+ end
30
+
31
+ def layout_id?(id)
32
+ self.layout_ids.include? id.to_sym
33
+ end
34
+
35
+ def layout
36
+ self.layouts ? self.layouts[self.layout_id] : nil
37
+ end
38
+
39
+ def load_layouts_from_yml_file(file)
40
+ self.layouts = YAML::load_file file
41
+ logger.info "loaded layouts from file #{file.path}"
42
+ logger.info "available layout(s): #{self.layout_ids.join(", ")}"
43
+ rescue Psych::SyntaxError => error
44
+ self.clear_layouts!
45
+ raise error
46
+ end
47
+
48
+ end
@@ -0,0 +1,14 @@
1
+
2
+ class Holoserve::History
3
+
4
+ attr_reader :pair_names
5
+
6
+ def initialize
7
+ @pair_names = [ ]
8
+ end
9
+
10
+ def clear!
11
+ @pair_names.clear
12
+ end
13
+
14
+ end
@@ -0,0 +1,74 @@
1
+ require 'sinatra'
2
+ require 'yaml'
3
+ require 'json'
4
+
5
+ class Holoserve::Interface::Control < Sinatra::Base
6
+
7
+ post "/_control/layouts" do
8
+ begin
9
+ configuration.load_layouts_from_yml_file params["file"][:tempfile]
10
+ respond_json_acknowledgement
11
+ rescue Psych::SyntaxError => error
12
+ error 400, error.inspect
13
+ end
14
+ end
15
+
16
+ delete "/_control/layouts" do
17
+ configuration.clear_layouts!
18
+ respond_json_acknowledgement
19
+ end
20
+
21
+ get "/_control/layouts/ids" do
22
+ respond_json configuration.layout_ids
23
+ end
24
+
25
+ put "/_control/layouts/:id/current" do |id|
26
+ if configuration.layout_id?(id)
27
+ configuration.layout_id = id
28
+ respond_json_acknowledgement
29
+ else
30
+ not_found
31
+ end
32
+ end
33
+
34
+ get "/_control/layouts/current" do
35
+ configuration.layout_id.to_s
36
+ end
37
+
38
+ get "/_control/bucket/requests" do
39
+ respond_json bucket.requests
40
+ end
41
+
42
+ get "/_control/history" do
43
+ respond_json history.pair_names
44
+ end
45
+
46
+ delete "/_control/history" do
47
+ history.clear!
48
+ respond_json_acknowledgement
49
+ end
50
+
51
+ private
52
+
53
+ def respond_json_acknowledgement
54
+ respond_json :ok => true
55
+ end
56
+
57
+ def respond_json(object)
58
+ content_type "application/json"
59
+ JSON.dump object
60
+ end
61
+
62
+ def bucket
63
+ Holoserve.instance.bucket
64
+ end
65
+
66
+ def history
67
+ Holoserve.instance.history
68
+ end
69
+
70
+ def configuration
71
+ Holoserve.instance.configuration
72
+ end
73
+
74
+ end
@@ -0,0 +1,47 @@
1
+ require 'pp'
2
+
3
+ class Holoserve::Interface::Fake
4
+
5
+ def call(env)
6
+ request = Holoserve::Request::Decomposer.new(env).hash
7
+ pair = Holoserve::Pair::Finder.new(configuration, request).pair
8
+ if pair
9
+ if name = pair[:name]
10
+ history.pair_names << name
11
+ logger.info "received handled request with name '#{name}'"
12
+ end
13
+ Holoserve::Response::Composer.new(pair[:response]).response_array
14
+ else
15
+ bucket.requests << request
16
+ logger.error "received unhandled request\n" + request.pretty_inspect
17
+ not_found
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def not_found
24
+ [ 404, { "Content-Type" => "text/plain" }, [ "no response found for this request" ] ]
25
+ end
26
+
27
+ def logger
28
+ configuration.logger
29
+ end
30
+
31
+ def layout
32
+ configuration.layout
33
+ end
34
+
35
+ def bucket
36
+ Holoserve.instance.bucket
37
+ end
38
+
39
+ def history
40
+ Holoserve.instance.history
41
+ end
42
+
43
+ def configuration
44
+ Holoserve.instance.configuration
45
+ end
46
+
47
+ end
@@ -0,0 +1,7 @@
1
+
2
+ module Holoserve::Interface
3
+
4
+ autoload :Control, File.join(File.dirname(__FILE__), "interface", "control")
5
+ autoload :Fake, File.join(File.dirname(__FILE__), "interface", "fake")
6
+
7
+ end
@@ -0,0 +1,21 @@
1
+
2
+ class Holoserve::Pair::Finder
3
+
4
+ def initialize(configuration, request)
5
+ @configuration, @request = configuration, request
6
+ end
7
+
8
+ def pair
9
+ return nil unless layout
10
+ layout.detect do |pair|
11
+ Holoserve::Request::Matcher.new(@request, pair[:request]).match?
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def layout
18
+ @configuration.layout
19
+ end
20
+
21
+ end
@@ -0,0 +1,6 @@
1
+
2
+ module Holoserve::Pair
3
+
4
+ autoload :Finder, File.join(File.dirname(__FILE__), "pair", "finder")
5
+
6
+ end
@@ -0,0 +1,65 @@
1
+
2
+ class Holoserve::Request::Decomposer
3
+
4
+ def initialize(request)
5
+ @request = request
6
+ end
7
+
8
+ def hash
9
+ hash = {
10
+ :method => @request["REQUEST_METHOD"],
11
+ :path => @request["PATH_INFO"]
12
+ }
13
+ hash.merge! :headers => headers unless headers.empty?
14
+ hash.merge! :body => body unless body.nil?
15
+ hash.merge! :parameters => parameters unless parameters.empty?
16
+ hash.merge! :oauth => oauth unless oauth.empty?
17
+ hash
18
+ end
19
+
20
+ private
21
+
22
+ def headers
23
+ headers = { }
24
+ @request.each do |key, value|
25
+ headers[ key.to_sym ] = value unless key =~ /^rack\./
26
+ end
27
+ headers
28
+ end
29
+
30
+ def body
31
+ @body ||= begin
32
+ body = @request["rack.input"].read
33
+ body && body != "" ? body : nil
34
+ end
35
+ end
36
+
37
+ def parameters
38
+ Holoserve::Tool::Hash::KeySymbolizer.new(query_hash.merge(form_hash)).hash
39
+ end
40
+
41
+ def query_hash
42
+ @request["rack.request.query_hash"] || { }
43
+ end
44
+
45
+ def form_hash
46
+ @request["rack.request.form_hash"] || { }
47
+ end
48
+
49
+ def oauth
50
+ @oauth ||= begin
51
+ oauth = { }
52
+ http_authorization = @request["HTTP_AUTHORIZATION"]
53
+ if http_authorization && http_authorization =~ /^OAuth/
54
+ http_authorization = http_authorization.sub /^OAuth/, ""
55
+ pairs = http_authorization.split ","
56
+ pairs.each do |pair|
57
+ key, value = *pair.strip.split("=")
58
+ oauth[ key.to_sym ] = value.sub(/^\\"/, "").sub(/\\"$/, "").gsub(/"/, "")
59
+ end
60
+ end
61
+ oauth
62
+ end
63
+ end
64
+
65
+ end
@@ -0,0 +1,61 @@
1
+
2
+ class Holoserve::Request::Matcher
3
+
4
+ def initialize(request, request_subset)
5
+ @request, @request_subset = request, request_subset
6
+ end
7
+
8
+ def match?
9
+ match_method? &&
10
+ match_path? &&
11
+ match_headers? &&
12
+ match_body? &&
13
+ match_parameters? &&
14
+ match_oauth?
15
+ end
16
+
17
+ private
18
+
19
+ def match_method?
20
+ @request_subset[:method] ?
21
+ @request[:method] == @request_subset[:method] :
22
+ true
23
+ end
24
+
25
+ def match_path?
26
+ @request_subset[:path] ?
27
+ @request[:path] == @request_subset[:path] :
28
+ true
29
+ end
30
+
31
+ def match_headers?
32
+ match = true
33
+ (@request_subset[:headers] || { }).each do |key, value|
34
+ match &&= @request[:headers][key] == value
35
+ end
36
+ match
37
+ end
38
+
39
+ def match_body?
40
+ @request_subset[:body] ?
41
+ @request[:body] == @request_subset[:body] :
42
+ true
43
+ end
44
+
45
+ def match_parameters?
46
+ match = true
47
+ (@request_subset[:parameters] || { }).each do |key, value|
48
+ match &&= @request[:parameters][key] == value
49
+ end
50
+ match
51
+ end
52
+
53
+ def match_oauth?
54
+ match = true
55
+ (@request_subset[:oauth] || { }).each do |key, value|
56
+ match &&= @request[:oauth][key] == value
57
+ end
58
+ match
59
+ end
60
+
61
+ end
@@ -0,0 +1,7 @@
1
+
2
+ module Holoserve::Request
3
+
4
+ autoload :Decomposer, File.join(File.dirname(__FILE__), "request", "decomposer")
5
+ autoload :Matcher, File.join(File.dirname(__FILE__), "request", "matcher")
6
+
7
+ end
@@ -0,0 +1,35 @@
1
+ require 'json'
2
+
3
+ class Holoserve::Response::Composer
4
+
5
+ def initialize(response)
6
+ @response = response
7
+ end
8
+
9
+ def response_array
10
+ [ status, headers, [ body ] ]
11
+ end
12
+
13
+ private
14
+
15
+ def status
16
+ @response[:status]
17
+ end
18
+
19
+ def headers
20
+ @response[:headers] || { }
21
+ end
22
+
23
+ def body
24
+ @body ||= begin
25
+ if @response.has_key?(:body)
26
+ @response[:body]
27
+ elsif @response.has_key?(:json)
28
+ JSON.dump @response[:json]
29
+ else
30
+ nil
31
+ end
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,6 @@
1
+
2
+ module Holoserve::Response
3
+
4
+ autoload :Composer, File.join(File.dirname(__FILE__), "response", "composer")
5
+
6
+ end
@@ -0,0 +1,68 @@
1
+ require 'unicorn'
2
+ require 'transport'
3
+
4
+ class Holoserve::Runner
5
+
6
+ attr_reader :port
7
+
8
+ def initialize(options = { })
9
+ @port = options[:port] || 4250
10
+ @layouts_filename = options[:layouts_filename]
11
+ @layout = options[:layout]
12
+
13
+ @rackup_options = Unicorn::Configurator::RACKUP
14
+ @rackup_options[:port] = @port
15
+ @rackup_options[:set_listener] = true
16
+ @options = @rackup_options[:options]
17
+
18
+ @unicorn = Unicorn::HttpServer.new rack, @options
19
+ end
20
+
21
+ def start
22
+ @unicorn.start
23
+ upload_layouts if @layouts_filename
24
+ set_layout if @layout
25
+ end
26
+
27
+ def join
28
+ return unless @unicorn
29
+ @unicorn.join
30
+ end
31
+
32
+ def stop
33
+ return unless @unicorn
34
+ @unicorn.stop
35
+ @unicorn = nil
36
+ end
37
+
38
+ def run
39
+ self.start
40
+ self.join
41
+ end
42
+
43
+ private
44
+
45
+ def upload_layouts
46
+ Holoserve::Tool::Uploader.new(
47
+ @layouts_filename,
48
+ :post,
49
+ "http://localhost:#{port}/_control/layouts",
50
+ :expected_status_code => 200
51
+ ).upload
52
+ nil
53
+ end
54
+
55
+ def set_layout
56
+ Transport::JSON.request :put, "http://localhost:#{port}/_control/layouts/#{@layout}/current", :expected_status_code => 200
57
+ nil
58
+ end
59
+
60
+ def rack
61
+ instance.rack
62
+ end
63
+
64
+ def instance
65
+ Holoserve.instance
66
+ end
67
+
68
+ end
@@ -0,0 +1,41 @@
1
+
2
+ class Holoserve::Tool::Hash::KeySymbolizer
3
+
4
+ def initialize(hash)
5
+ @hash = hash
6
+ end
7
+
8
+ def hash
9
+ symbolize_keys @hash
10
+ end
11
+
12
+ private
13
+
14
+ def symbolize_keys(hash)
15
+ return nil unless hash.is_a?(Hash)
16
+ result = { }
17
+ hash.each do |key, value|
18
+ result[ key.to_sym ] = if value.is_a?(Hash)
19
+ symbolize_keys value
20
+ elsif value.is_a?(Array)
21
+ symbolize_keys_of_all value
22
+ else
23
+ value
24
+ end
25
+ end
26
+ result
27
+ end
28
+
29
+ def symbolize_keys_of_all(array)
30
+ array.map do |item|
31
+ if item.is_a?(Hash)
32
+ symbolize_keys item
33
+ elsif item.is_a?(Array)
34
+ symbolize_keys_of_all item
35
+ else
36
+ item
37
+ end
38
+ end
39
+ end
40
+
41
+ end
@@ -0,0 +1,6 @@
1
+
2
+ module Holoserve::Tool::Hash
3
+
4
+ autoload :KeySymbolizer, File.join(File.dirname(__FILE__), "hash", "key_symbolizer")
5
+
6
+ end
@@ -0,0 +1,36 @@
1
+ require 'transport'
2
+
3
+ class Holoserve::Tool::Uploader
4
+
5
+ def initialize(filename, http_method, url, options = { })
6
+ @filename, @http_method, @url, @options = filename, http_method, url, options
7
+ end
8
+
9
+ def upload
10
+ options = @options
11
+ options[:headers] = (@options[:headers] || { }).merge(headers)
12
+ options[:body] = body
13
+ Transport::HTTP.request @http_method, @url, options
14
+ end
15
+
16
+ private
17
+
18
+ def headers
19
+ { "Content-Type" => "multipart/form-data, boundary=#{boundary}" }
20
+ end
21
+
22
+ def body
23
+ "--#{boundary}\r\n" +
24
+ "Content-Disposition: form-data; name=\"file\"; filename=\"#{File.basename(@filename)}\"\r\n" +
25
+ "Content-Type: application/x-yaml\r\n" +
26
+ "\r\n" +
27
+ File.read(@filename) +
28
+ "\r\n" +
29
+ "--#{boundary}--\r\n"
30
+ end
31
+
32
+ def boundary
33
+ "xxx12345xxx"
34
+ end
35
+
36
+ end
@@ -0,0 +1,7 @@
1
+
2
+ module Holoserve::Tool
3
+
4
+ autoload :Hash, File.join(File.dirname(__FILE__), "tool", "hash")
5
+ autoload :Uploader, File.join(File.dirname(__FILE__), "tool", "uploader")
6
+
7
+ end
data/lib/holoserve.rb ADDED
@@ -0,0 +1,59 @@
1
+ require 'rack/builder'
2
+ require 'logger'
3
+
4
+ class Holoserve
5
+
6
+ autoload :Bucket, File.join(File.dirname(__FILE__), "holoserve", "bucket")
7
+ autoload :Configuration, File.join(File.dirname(__FILE__), "holoserve", "configuration")
8
+ autoload :History, File.join(File.dirname(__FILE__), "holoserve", "history")
9
+ autoload :Interface, File.join(File.dirname(__FILE__), "holoserve", "interface")
10
+ autoload :Pair, File.join(File.dirname(__FILE__), "holoserve", "pair")
11
+ autoload :Request, File.join(File.dirname(__FILE__), "holoserve", "request")
12
+ autoload :Response, File.join(File.dirname(__FILE__), "holoserve", "response")
13
+ autoload :Runner, File.join(File.dirname(__FILE__), "holoserve", "runner")
14
+ autoload :Tool, File.join(File.dirname(__FILE__), "holoserve", "tool")
15
+
16
+ attr_reader :logger
17
+ attr_reader :configuration
18
+ attr_reader :bucket
19
+ attr_reader :history
20
+ attr_reader :rack
21
+
22
+ def initialize
23
+ initialize_logger
24
+ initialize_configuration
25
+ initialize_bucket
26
+ initialize_history
27
+ initialize_rack
28
+ end
29
+
30
+ private
31
+
32
+ def initialize_logger
33
+ @logger = Logger.new STDOUT
34
+ end
35
+
36
+ def initialize_configuration
37
+ @configuration = Configuration.new @logger
38
+ end
39
+
40
+ def initialize_bucket
41
+ @bucket = Bucket.new
42
+ end
43
+
44
+ def initialize_history
45
+ @history = History.new
46
+ end
47
+
48
+ def initialize_rack
49
+ @rack = Rack::Builder.new do
50
+ use Interface::Control
51
+ run Interface::Fake.new
52
+ end
53
+ end
54
+
55
+ def self.instance
56
+ @instance ||= self.new
57
+ end
58
+
59
+ end
metadata ADDED
@@ -0,0 +1,159 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: holoserve
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Philipp Brüll
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rack
16
+ requirement: &70258362918600 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70258362918600
25
+ - !ruby/object:Gem::Dependency
26
+ name: sinatra
27
+ requirement: &70258366899940 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70258366899940
36
+ - !ruby/object:Gem::Dependency
37
+ name: unicorn
38
+ requirement: &70258366896080 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70258366896080
47
+ - !ruby/object:Gem::Dependency
48
+ name: transport
49
+ requirement: &70258366894700 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70258366894700
58
+ - !ruby/object:Gem::Dependency
59
+ name: cucumber
60
+ requirement: &70258366892660 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70258366892660
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: &70258366891240 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *70258366891240
80
+ - !ruby/object:Gem::Dependency
81
+ name: transport
82
+ requirement: &70258366890200 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *70258366890200
91
+ - !ruby/object:Gem::Dependency
92
+ name: oauth
93
+ requirement: &70258366889100 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *70258366889100
102
+ description: This tool can be used to fake webservice APIs for testing proposals.
103
+ email: philipp.bruell@skrill.com
104
+ executables: []
105
+ extensions: []
106
+ extra_rdoc_files:
107
+ - README.rdoc
108
+ files:
109
+ - README.rdoc
110
+ - LICENSE
111
+ - Rakefile
112
+ - bin/holoserve
113
+ - lib/holoserve/bucket.rb
114
+ - lib/holoserve/configuration.rb
115
+ - lib/holoserve/history.rb
116
+ - lib/holoserve/interface/control.rb
117
+ - lib/holoserve/interface/fake.rb
118
+ - lib/holoserve/interface.rb
119
+ - lib/holoserve/pair/finder.rb
120
+ - lib/holoserve/pair.rb
121
+ - lib/holoserve/request/decomposer.rb
122
+ - lib/holoserve/request/matcher.rb
123
+ - lib/holoserve/request.rb
124
+ - lib/holoserve/response/composer.rb
125
+ - lib/holoserve/response.rb
126
+ - lib/holoserve/runner.rb
127
+ - lib/holoserve/tool/hash/key_symbolizer.rb
128
+ - lib/holoserve/tool/hash.rb
129
+ - lib/holoserve/tool/uploader.rb
130
+ - lib/holoserve/tool.rb
131
+ - lib/holoserve.rb
132
+ homepage: http://github.com/skrill/holoserve
133
+ licenses: []
134
+ post_install_message:
135
+ rdoc_options: []
136
+ require_paths:
137
+ - lib
138
+ required_ruby_version: !ruby/object:Gem::Requirement
139
+ none: false
140
+ requirements:
141
+ - - ! '>='
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ segments:
145
+ - 0
146
+ hash: 3189243162952921439
147
+ required_rubygems_version: !ruby/object:Gem::Requirement
148
+ none: false
149
+ requirements:
150
+ - - ! '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ requirements: []
154
+ rubyforge_project: holoserve
155
+ rubygems_version: 1.8.10
156
+ signing_key:
157
+ specification_version: 3
158
+ summary: Tool to fake HTTP APIs.
159
+ test_files: []