holoserve 0.1.0

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.
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: []