holoserve 0.1.1 → 0.2.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/README.rdoc CHANGED
@@ -7,12 +7,13 @@ run faster and be independent from other API and network problems.
7
7
  == Concept
8
8
 
9
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.
10
+ the server layout. If a match is found, the defined static response is returned. The response is defined by a default
11
+ and the current server situation.
12
+ The name of the matched request/response pair is saved in a request history. If no match is found, a 404 is returned and
13
+ the request data is stored in the bucket for unhandeled requests. These informations can be used to extend the server
14
+ layout with missing request handlers.
14
15
 
15
- Layouts, history and bucket can be accessed via control routes, which are described below.
16
+ The layout, situation, history and bucket can be accessed via control routes, which are described below.
16
17
 
17
18
  == Installation
18
19
 
@@ -26,43 +27,40 @@ To start up an empty Holoserve instance, type...
26
27
 
27
28
  holoserve
28
29
 
29
- To load and select a server layout during start up, you can provide some parameters.
30
+ To load a server layout and define a situation during start up, use these parameters.
30
31
 
31
- holoserve -f layouts.yml -l backend
32
+ holoserve -l layouts.yml -s backend_without_users
32
33
 
33
34
  == Control routes
34
35
 
35
36
  If you're using Ruby, you can control Holoserve via the
36
37
  {Holoserve Connector}[https://github.com/skrill/holoserve-connector] gem.
37
38
 
38
- === POST /_control/layouts
39
+ === POST /_control/layout.:format
39
40
 
40
- It should receive a parameter named <tt>file</tt> that contains an uploaded YML file with the server layouts. See
41
- {YML server layouts file format}[rdoc-label:YML-server-layouts-file-format] below.
41
+ It should receive a parameter named <tt>file</tt> that contains a file with the server layout. The format of the file
42
+ should fit the specified format. The format can be <tt>yaml</tt> or <tt>json</tt>. See
43
+ {Layout file format}[rdoc-label:Layout-file-format] below.
42
44
 
43
- === DELETE /_control/layouts
45
+ === GET /_control/layout.:format
44
46
 
45
- Removes all server layouts.
47
+ Returns the server layout in the requested format.
46
48
 
47
- === GET /_control/layouts/ids
49
+ === DELETE /_control/layout
48
50
 
49
- Returns the list of ids of all server layouts.
51
+ Removes the server layout.
50
52
 
51
- ==== Response example
52
-
53
- [ "one", "two" ]
53
+ === PUT /_control/situation/:name
54
54
 
55
- === PUT /_control/layouts/:id/current
55
+ Sets the current situation.
56
56
 
57
- Makes the layout with id <tt>:id</tt> the currently active one.
57
+ === GET /_control/situation
58
58
 
59
- === GET /_control/layouts/current
60
-
61
- Returns the id of the current layout.
59
+ Returns the name of the current situation.
62
60
 
63
61
  ==== Response example
64
62
 
65
- one
63
+ backend_without_users
66
64
 
67
65
  === GET /_control/bucket/requests
68
66
 
@@ -106,40 +104,42 @@ Returns a list of all names of pairs that has been triggered.
106
104
 
107
105
  Removes all entries from the history.
108
106
 
109
- == YML server layouts file format
110
-
111
- The server layouts file should have the following format.
112
-
113
- one:
114
- -
115
- name: "test_received"
116
- request:
117
- method: "POST"
118
- path: "/test"
119
- headers:
120
- HTTP_USER_AGENT: "Ruby"
121
- HTTP_AUTHORIZATION: "OAuth oauth_token=12345"
122
- body:
123
- "test=value"
124
- parameters:
125
- test: "value"
126
- oauth:
127
- oauth_token: "12345"
128
- response:
107
+ == Layout file format
108
+
109
+ The server layout file should have the following format.
110
+
111
+ -
112
+ name: "test_received"
113
+ request:
114
+ method: "POST"
115
+ path: "/test"
116
+ headers:
117
+ HTTP_USER_AGENT: "Ruby"
118
+ HTTP_AUTHORIZATION: "OAuth oauth_token=12345"
119
+ body:
120
+ "test=value"
121
+ parameters:
122
+ test: "value"
123
+ oauth:
124
+ oauth_token: "12345"
125
+ responses:
126
+ default:
129
127
  status: 200
128
+ one:
130
129
  body:
131
130
  "ok"
132
- -
133
- request:
134
- method: "GET"
135
- path: "/test"
136
- response:
131
+ -
132
+ request:
133
+ method: "GET"
134
+ path: "/test"
135
+ responses:
136
+ default:
137
137
  status: 200
138
138
  body:
139
139
  "ok too"
140
140
 
141
- This example would define a server layout named <tt>one</tt> that has two request/response pairs. The first pair would
142
- have the name <tt>test_received</tt> and would match a <tt>POST</tt> request to the path <tt>/test</tt>.
141
+ This example would define a server layout that has two request/response pairs. The first pair would have the name
142
+ <tt>test_received</tt> and would match a <tt>POST</tt> request to the path <tt>/test</tt>.
143
143
 
144
144
  Notice, that the given request attributes are the _minimal_ values that have to match the incomong one. An incoming
145
145
  request may has much more attributes (see bucket example above). If a request is matched, the corresponding pair name is
@@ -148,3 +148,6 @@ placed in the history.
148
148
  As the sections <tt>headers</tt> and <tt>body</tt> are raw values from the request, the sections <tt>parameters</tt> and
149
149
  <tt>oauth</tt> contain high-level values. The <tt>parameters</tt> hash is taken from the request body or the query and
150
150
  the hash containing the OAuth values is parsed from a fitting <tt>HTTP_AUTHORIZATION</tt> header.
151
+
152
+ The response is defined in the <tt>responses</tt> section of each pair. The server will response a merge of default
153
+ response and the response defined by the current situation.
data/Rakefile CHANGED
@@ -27,6 +27,13 @@ begin
27
27
  rescue LoadError
28
28
  end
29
29
 
30
+ begin
31
+ require 'rspec/core/rake_task'
32
+
33
+ RSpec::Core::RakeTask.new
34
+ rescue LoadError
35
+ end
36
+
30
37
  begin
31
38
  require 'rdoc'
32
39
  require 'rdoc/task'
data/bin/holoserve CHANGED
@@ -14,11 +14,11 @@ OptionParser.new do |parser|
14
14
  parser.on("-p", "--port PORT", Integer, "The port holoserve should listen to.") do |value|
15
15
  options[:port] = value
16
16
  end
17
- parser.on("-f", "--layouts-file FILE", "Load the specified layouts file on startup.") do |value|
18
- options[:layouts_filename] = value
17
+ parser.on("-l", "--layout-file FILE", "Load the specified layout file on startup.") do |value|
18
+ options[:layout_filename] = value
19
19
  end
20
- parser.on("-l", "--layout LAYOUT", "Selects the specified layout as the current one.") do |value|
21
- options[:layout] = value
20
+ parser.on("-s", "--situation SITUATION", "Sets the situation.") do |value|
21
+ options[:situation] = value
22
22
  end
23
23
  parser.on_tail("-h", "--help", "Shows the help message.") do
24
24
  puts parser
@@ -2,47 +2,44 @@ require 'yaml'
2
2
 
3
3
  class Holoserve::Configuration
4
4
 
5
+ class InvalidFormatError < StandardError; end
6
+
5
7
  attr_reader :logger
6
8
 
7
- attr_reader :layouts
8
- attr_reader :layout_id
9
+ attr_reader :layout
10
+ attr_reader :situation
9
11
 
10
12
  def initialize(logger)
11
13
  @logger = logger
12
14
  end
13
15
 
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"
16
+ def layout=(hash_or_array)
17
+ @layout = Holoserve::Tool::Hash::KeySymbolizer.new(hash_or_array).hash
21
18
  end
22
19
 
23
- def clear_layouts!
24
- self.layouts = nil
20
+ def situation=(value)
21
+ @situation = value.to_sym
22
+ logger.info "made '#{value}' the current situation"
25
23
  end
26
24
 
27
- def layout_ids
28
- self.layouts ? self.layouts.keys : [ ]
25
+ def clear_layout!
26
+ self.layout = nil
29
27
  end
30
28
 
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
29
+ def load_layout_from_yaml_file(file)
30
+ self.layout = YAML::load_file file
31
+ logger.info "loaded layouts from yaml file #{file.path}"
32
+ rescue Psych::SyntaxError => error
33
+ self.clear_layout!
34
+ raise InvalidFormatError, error.to_s
37
35
  end
38
36
 
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
37
+ def load_layout_from_json_file(file)
38
+ self.layout = JSON.parse File.read(file)
39
+ logger.info "loaded layouts from json file #{file.path}"
40
+ rescue JSON::ParserError => error
41
+ self.clear_layout!
42
+ raise InvalidFormatError, error.to_s
46
43
  end
47
44
 
48
45
  end
@@ -4,35 +4,47 @@ require 'json'
4
4
 
5
5
  class Holoserve::Interface::Control < Sinatra::Base
6
6
 
7
- post "/_control/layouts" do
7
+ mime_type :yaml, "application/x-yaml"
8
+ mime_type :json, "application/json"
9
+
10
+ post "/_control/layout.:format" do |format|
8
11
  begin
9
- configuration.load_layouts_from_yml_file params["file"][:tempfile]
10
- respond_json_acknowledgement
11
- rescue Psych::SyntaxError => error
12
+ if format == "yaml"
13
+ configuration.load_layout_from_yaml_file params["file"][:tempfile]
14
+ respond_json_acknowledgement
15
+ elsif format == "json"
16
+ configuration.load_layout_from_json_file params["file"][:tempfile]
17
+ respond_json_acknowledgement
18
+ else
19
+ not_acceptable
20
+ end
21
+ rescue Holoserve::Configuration::InvalidFormatError => error
12
22
  error 400, error.inspect
13
23
  end
14
24
  end
15
25
 
16
- delete "/_control/layouts" do
17
- configuration.clear_layouts!
18
- respond_json_acknowledgement
26
+ get "/_control/layout.:format" do |format|
27
+ if format == "yaml"
28
+ respond_yaml configuration.layout
29
+ elsif format == "json"
30
+ respond_json configuration.layout
31
+ else
32
+ not_acceptable
33
+ end
19
34
  end
20
35
 
21
- get "/_control/layouts/ids" do
22
- respond_json configuration.layout_ids
36
+ delete "/_control/layout" do
37
+ configuration.clear_layout!
38
+ respond_json_acknowledgement
23
39
  end
24
40
 
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
41
+ put "/_control/situation/:name" do |situation|
42
+ configuration.situation = situation
43
+ respond_json_acknowledgement
32
44
  end
33
45
 
34
- get "/_control/layouts/current" do
35
- configuration.layout_id.to_s
46
+ get "/_control/situation" do
47
+ configuration.situation.to_s
36
48
  end
37
49
 
38
50
  get "/_control/bucket/requests" do
@@ -55,10 +67,19 @@ class Holoserve::Interface::Control < Sinatra::Base
55
67
  end
56
68
 
57
69
  def respond_json(object)
58
- content_type "application/json"
70
+ content_type :json
59
71
  JSON.dump object
60
72
  end
61
73
 
74
+ def respond_yaml(object)
75
+ content_type :yaml
76
+ object.to_yaml
77
+ end
78
+
79
+ def not_acceptable
80
+ [ 406, { }, [ "format not acceptable" ] ]
81
+ end
82
+
62
83
  def bucket
63
84
  Holoserve.instance.bucket
64
85
  end
@@ -10,7 +10,14 @@ class Holoserve::Interface::Fake
10
10
  history.pair_names << name
11
11
  logger.info "received handled request with name '#{name}'"
12
12
  end
13
- Holoserve::Response::Composer.new(pair[:response]).response_array
13
+ responses = pair[:responses]
14
+ response = Holoserve::Tool::Merger.new(responses[:default] || { }, responses[configuration.situation.to_sym] || { }).result
15
+ if response.empty?
16
+ logger.warn "received request #{pair[:name]} with undefined response"
17
+ not_found
18
+ else
19
+ Holoserve::Response::Composer.new(response).response_array
20
+ end
14
21
  else
15
22
  bucket.requests << request
16
23
  logger.error "received unhandled request\n" + request.pretty_inspect
@@ -45,7 +45,7 @@ class Holoserve::Request::Matcher
45
45
  def match_parameters?
46
46
  match = true
47
47
  (@request_subset[:parameters] || { }).each do |key, value|
48
- match &&= @request[:parameters][key] == value
48
+ match &&= @request[:parameters].is_a?(Hash) && (@request[:parameters][key] == value)
49
49
  end
50
50
  match
51
51
  end
@@ -53,7 +53,7 @@ class Holoserve::Request::Matcher
53
53
  def match_oauth?
54
54
  match = true
55
55
  (@request_subset[:oauth] || { }).each do |key, value|
56
- match &&= @request[:oauth][key] == value
56
+ match &&= @request[:oauth].is_a?(Hash) && (@request[:oauth][key] == value)
57
57
  end
58
58
  match
59
59
  end
@@ -7,8 +7,8 @@ class Holoserve::Runner
7
7
 
8
8
  def initialize(options = { })
9
9
  @port = options[:port] || 4250
10
- @layouts_filename = options[:layouts_filename]
11
- @layout = options[:layout]
10
+ @layout_filename = options[:layout_filename]
11
+ @situation = options[:situation]
12
12
 
13
13
  @rackup_options = Unicorn::Configurator::RACKUP
14
14
  @rackup_options[:port] = @port
@@ -20,8 +20,8 @@ class Holoserve::Runner
20
20
 
21
21
  def start
22
22
  @unicorn.start
23
- upload_layouts if @layouts_filename
24
- set_layout if @layout
23
+ upload_layout if @layout_filename
24
+ set_situation if @situation
25
25
  end
26
26
 
27
27
  def join
@@ -42,18 +42,22 @@ class Holoserve::Runner
42
42
 
43
43
  private
44
44
 
45
- def upload_layouts
45
+ def upload_layout
46
+ format = File.extname(@layout_filename).sub(/^\./, "")
47
+ raise ArgumentError, "file extension indicates wrong format '#{format}' (choose yaml or json)" unless [ "yaml", "json" ].include?(format)
46
48
  Holoserve::Tool::Uploader.new(
47
- @layouts_filename,
49
+ @layout_filename,
48
50
  :post,
49
- "http://localhost:#{port}/_control/layouts",
51
+ "http://localhost:#{port}/_control/layout.#{format}",
50
52
  :expected_status_code => 200
51
53
  ).upload
52
54
  nil
53
55
  end
54
56
 
55
- def set_layout
56
- Transport::JSON.request :put, "http://localhost:#{port}/_control/layouts/#{@layout}/current", :expected_status_code => 200
57
+ def set_situation
58
+ Transport::JSON.request :put,
59
+ "http://localhost:#{port}/_control/situation/#{@situation}",
60
+ :expected_status_code => 200
57
61
  nil
58
62
  end
59
63
 
@@ -1,12 +1,18 @@
1
1
 
2
2
  class Holoserve::Tool::Hash::KeySymbolizer
3
3
 
4
- def initialize(hash)
5
- @hash = hash
4
+ def initialize(hash_or_array)
5
+ @hash_or_array = hash_or_array
6
6
  end
7
7
 
8
8
  def hash
9
- symbolize_keys @hash
9
+ if @hash_or_array.is_a?(Hash)
10
+ symbolize_keys @hash_or_array
11
+ elsif @hash_or_array.is_a?(Array)
12
+ symbolize_keys_of_all @hash_or_array
13
+ else
14
+ @hash_or_array
15
+ end
10
16
  end
11
17
 
12
18
  private
@@ -27,6 +33,7 @@ class Holoserve::Tool::Hash::KeySymbolizer
27
33
  end
28
34
 
29
35
  def symbolize_keys_of_all(array)
36
+ return nil unless array.is_a?(Array)
30
37
  array.map do |item|
31
38
  if item.is_a?(Hash)
32
39
  symbolize_keys item
@@ -0,0 +1,51 @@
1
+
2
+ class Holoserve::Tool::Merger
3
+
4
+ def initialize(hash_or_array_one, hash_or_array_two)
5
+ @hash_or_array_one, @hash_or_array_two = hash_or_array_one, hash_or_array_two
6
+ end
7
+
8
+ def result
9
+ if @hash_or_array_one.is_a?(Hash) && @hash_or_array_two.is_a?(Hash)
10
+ merged_hash
11
+ elsif @hash_or_array_one.is_a?(Array) && @hash_or_array_two.is_a?(Array)
12
+ merged_array
13
+ else
14
+ @hash_or_array_two
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def merged_hash
21
+ result = { }
22
+ (@hash_or_array_one.keys + @hash_or_array_two.keys).uniq.each do |key|
23
+ value_one, value_two = @hash_or_array_one[key],@hash_or_array_two[key]
24
+ result[key] = if values_mergeable?(value_one, value_two)
25
+ self.class.new(value_one, value_two).result
26
+ else
27
+ value_two || value_one
28
+ end
29
+ end
30
+ result
31
+ end
32
+
33
+ def merged_array
34
+ result = Array.new [ @hash_or_array_one.length, @hash_or_array_two.length ].max
35
+ result.each_index do |index|
36
+ value_one, value_two = @hash_or_array_one[index], @hash_or_array_two[index]
37
+ result[index] = if values_mergeable?(value_one, value_two)
38
+ self.class.new(value_one, value_two).result
39
+ else
40
+ value_two || value_one
41
+ end
42
+ end
43
+ result
44
+ end
45
+
46
+ def values_mergeable?(value_one, value_two)
47
+ (value_one.is_a?(Hash) && value_two.is_a?(Hash)) ||
48
+ (value_one.is_a?(Array) && value_two.is_a?(Array))
49
+ end
50
+
51
+ end
@@ -2,6 +2,7 @@
2
2
  module Holoserve::Tool
3
3
 
4
4
  autoload :Hash, File.join(File.dirname(__FILE__), "tool", "hash")
5
+ autoload :Merger, File.join(File.dirname(__FILE__), "tool", "merger")
5
6
  autoload :Uploader, File.join(File.dirname(__FILE__), "tool", "uploader")
6
7
 
7
8
  end
data/spec/helper.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'rspec'
2
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "lib", "holoserve"))
@@ -0,0 +1,61 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "helper"))
2
+
3
+ describe Holoserve::Tool::Merger do
4
+
5
+ subject { described_class.new argument_one, argument_two }
6
+
7
+ describe "result" do
8
+
9
+ context "of two hashes" do
10
+
11
+ let(:argument_one) { { :test => "value" } }
12
+ let(:argument_two) { { :test => "another value" } }
13
+
14
+ it "should merge nested values" do
15
+ subject.result.should == { :test => "another value" }
16
+ end
17
+
18
+ end
19
+
20
+ context "of two nested hashes" do
21
+
22
+ let(:argument_one) { { :test => { :regular =>"value", :nested => "value" } } }
23
+ let(:argument_two) { { :test => { :nested => "another value" } } }
24
+
25
+ it "should merge nested values" do
26
+ subject.result.should == { :test => { :regular => "value", :nested => "another value" } }
27
+ end
28
+
29
+ end
30
+
31
+ context "of two arrays" do
32
+
33
+ let(:argument_one) { [ { :test => "value" }, { :regular => "value", :another_test => "value" } ] }
34
+ let(:argument_two) { [ { :test => "another value" }, { :another_test => "another value" } ] }
35
+
36
+ it "should merge all nested hashes" do
37
+ subject.result.should == [
38
+ { :test => "another value" },
39
+ { :regular => "value", :another_test => "another value" }
40
+ ]
41
+ end
42
+
43
+ end
44
+
45
+ context "of two arrays with different lengths" do
46
+
47
+ let(:argument_one) { [ { :test => "value" } ] }
48
+ let(:argument_two) { [ { :test => "another value" }, { :another_test => "another value" } ] }
49
+
50
+ it "should merge all nested hashes" do
51
+ subject.result.should == [
52
+ { :test => "another value" },
53
+ { :another_test => "another value" }
54
+ ]
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+
61
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: holoserve
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-12-19 00:00:00.000000000 Z
12
+ date: 2012-01-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
16
- requirement: &70292202591440 !ruby/object:Gem::Requirement
16
+ requirement: &70247827043580 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70292202591440
24
+ version_requirements: *70247827043580
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: sinatra
27
- requirement: &70292202590900 !ruby/object:Gem::Requirement
27
+ requirement: &70247827042840 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70292202590900
35
+ version_requirements: *70247827042840
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: unicorn
38
- requirement: &70292202590400 !ruby/object:Gem::Requirement
38
+ requirement: &70247827042360 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70292202590400
46
+ version_requirements: *70247827042360
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: transport
49
- requirement: &70292202589740 !ruby/object:Gem::Requirement
49
+ requirement: &70247827041740 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *70292202589740
57
+ version_requirements: *70247827041740
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: cucumber
60
- requirement: &70292202589060 !ruby/object:Gem::Requirement
60
+ requirement: &70247827041200 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70292202589060
68
+ version_requirements: *70247827041200
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
- requirement: &70292202588380 !ruby/object:Gem::Requirement
71
+ requirement: &70247827040660 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '0'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70292202588380
79
+ version_requirements: *70247827040660
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: transport
82
- requirement: &70292202587840 !ruby/object:Gem::Requirement
82
+ requirement: &70247827040180 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: '0'
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *70292202587840
90
+ version_requirements: *70247827040180
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: oauth
93
- requirement: &70292202587300 !ruby/object:Gem::Requirement
93
+ requirement: &70247827039660 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ! '>='
@@ -98,7 +98,7 @@ dependencies:
98
98
  version: '0'
99
99
  type: :development
100
100
  prerelease: false
101
- version_requirements: *70292202587300
101
+ version_requirements: *70247827039660
102
102
  description: This tool can be used to fake webservice APIs for testing proposals.
103
103
  email: philipp.bruell@skrill.com
104
104
  executables:
@@ -127,9 +127,12 @@ files:
127
127
  - lib/holoserve/runner.rb
128
128
  - lib/holoserve/tool/hash/key_symbolizer.rb
129
129
  - lib/holoserve/tool/hash.rb
130
+ - lib/holoserve/tool/merger.rb
130
131
  - lib/holoserve/tool/uploader.rb
131
132
  - lib/holoserve/tool.rb
132
133
  - lib/holoserve.rb
134
+ - spec/helper.rb
135
+ - spec/lib/holoserve/tool/merger_spec.rb
133
136
  homepage: http://github.com/skrill/holoserve
134
137
  licenses: []
135
138
  post_install_message:
@@ -144,7 +147,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
144
147
  version: '0'
145
148
  segments:
146
149
  - 0
147
- hash: 1129492518361910852
150
+ hash: 460979945885990413
148
151
  required_rubygems_version: !ruby/object:Gem::Requirement
149
152
  none: false
150
153
  requirements:
@@ -157,4 +160,5 @@ rubygems_version: 1.8.10
157
160
  signing_key:
158
161
  specification_version: 3
159
162
  summary: Tool to fake HTTP APIs.
160
- test_files: []
163
+ test_files:
164
+ - spec/lib/holoserve/tool/merger_spec.rb