anmo2 0.0.31
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.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +112 -0
- data/.ruby-version +1 -0
- data/.rvmrc +1 -0
- data/.travis.yml +4 -0
- data/Dockerfile +9 -0
- data/Gemfile +4 -0
- data/README.md +41 -0
- data/Rakefile +20 -0
- data/anmo.gemspec +31 -0
- data/features/api.feature +204 -0
- data/features/step_definitions/api_steps.rb +87 -0
- data/features/support/env.rb +13 -0
- data/lib/anmo.rb +57 -0
- data/lib/anmo/application.rb +212 -0
- data/lib/anmo/version.rb +3 -0
- data/run.sh +8 -0
- data/test/anmo/application_test.rb +201 -0
- metadata +191 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c7310ced86159b67bd2c3341222a7e33b2a135f0
|
4
|
+
data.tar.gz: bfb7d3107fc3e36ddd6f65daaa84e91ae8ae8474
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 88d83b15443d8152dd009b7501a3e4c2f0b13dd1dfcb418826236a474b30184cf7bf1a9339b166c9e48e616c049e5d7a6a79f3269adac3bf342bc60069d76bb0
|
7
|
+
data.tar.gz: 00e8186c8d03d2fd29fb3b8fa7dab14a56ab7d9a54007726083a76cec88c3349b286a604707ddddd38f0080d31bbd64a545534abbf888b785fd84277a980363d
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
Lint/SpaceBeforeFirstArg:
|
2
|
+
Severity: fatal
|
3
|
+
Enabled: true
|
4
|
+
|
5
|
+
Lint/RescueException:
|
6
|
+
Severity: fatal
|
7
|
+
Enabled: true
|
8
|
+
|
9
|
+
Lint/UnusedBlockArgument:
|
10
|
+
Severity: fatal
|
11
|
+
Enabled: true
|
12
|
+
|
13
|
+
Metrics/LineLength:
|
14
|
+
Severity: fatal
|
15
|
+
Enabled: true
|
16
|
+
|
17
|
+
Metrics/MethodLength:
|
18
|
+
Severity: convention
|
19
|
+
Enabled: true
|
20
|
+
Max: 5
|
21
|
+
|
22
|
+
Style/AlignHash:
|
23
|
+
Severity: fatal
|
24
|
+
Enabled: true
|
25
|
+
EnforcedHashRocketStyle: table
|
26
|
+
|
27
|
+
Style/AlignParameters:
|
28
|
+
Severity: fatal
|
29
|
+
Enabled: true
|
30
|
+
EnforcedStyle: with_fixed_indentation
|
31
|
+
|
32
|
+
Style/BracesAroundHashParameters:
|
33
|
+
Severity: fatal
|
34
|
+
Enabled: true
|
35
|
+
|
36
|
+
Style/ConstantName:
|
37
|
+
Severity: fatal
|
38
|
+
Enabled: true
|
39
|
+
|
40
|
+
Style/Documentation:
|
41
|
+
Enabled: false
|
42
|
+
|
43
|
+
Style/EmptyLinesAroundClassBody:
|
44
|
+
Severity: fatal
|
45
|
+
Enabled: true
|
46
|
+
|
47
|
+
Style/ExtraSpacing:
|
48
|
+
Enabled: false
|
49
|
+
|
50
|
+
Style/FileName:
|
51
|
+
Enabled: false
|
52
|
+
|
53
|
+
Style/HashSyntax:
|
54
|
+
Severity: fatal
|
55
|
+
Enabled: true
|
56
|
+
EnforcedStyle: hash_rockets
|
57
|
+
|
58
|
+
Style/IndentationConsistency:
|
59
|
+
Severity: fatal
|
60
|
+
Enabled: true
|
61
|
+
|
62
|
+
Style/IndentationWidth:
|
63
|
+
Severity: fatal
|
64
|
+
Enabled: true
|
65
|
+
|
66
|
+
Style/IndentHash:
|
67
|
+
Severity: fatal
|
68
|
+
Enabled: true
|
69
|
+
|
70
|
+
Style/InlineComment:
|
71
|
+
Severity: fatal
|
72
|
+
Enabled: true
|
73
|
+
|
74
|
+
Style/LineEndConcatenation:
|
75
|
+
Severity: fatal
|
76
|
+
Enabled: true
|
77
|
+
|
78
|
+
Style/MethodName:
|
79
|
+
Severity: fatal
|
80
|
+
Enabled: true
|
81
|
+
|
82
|
+
Style/MultilineTernaryOperator:
|
83
|
+
Enabled: false
|
84
|
+
|
85
|
+
Style/NegatedIf:
|
86
|
+
Severity: fatal
|
87
|
+
Enabled: true
|
88
|
+
|
89
|
+
Style/Not:
|
90
|
+
Severity: fatal
|
91
|
+
Enabled: true
|
92
|
+
|
93
|
+
Style/SpaceInsideHashLiteralBraces:
|
94
|
+
Severity: fatal
|
95
|
+
Enabled: true
|
96
|
+
|
97
|
+
Style/StringLiterals:
|
98
|
+
Severity: fatal
|
99
|
+
Enabled: true
|
100
|
+
EnforcedStyle: double_quotes
|
101
|
+
|
102
|
+
Style/TrailingBlankLines:
|
103
|
+
Severity: fatal
|
104
|
+
Enabled: true
|
105
|
+
|
106
|
+
Style/TrailingWhitespace:
|
107
|
+
Severity: fatal
|
108
|
+
Enabled: true
|
109
|
+
|
110
|
+
Style/VariableName:
|
111
|
+
Severity: fatal
|
112
|
+
Enabled: true
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.1.2
|
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm ruby-1.9.2-p318
|
data/.travis.yml
ADDED
data/Dockerfile
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
Anmo
|
2
|
+
====
|
3
|
+
|
4
|
+
[](https://travis-ci.org/sthulbourn/anmo)
|
5
|
+
|
6
|
+
What?
|
7
|
+
-----
|
8
|
+
Anmo acts as a mock api and can be used to store arbitrary pieces of data for integration testing flaky APIs.
|
9
|
+
This is generally *not a good idea*, but where you can't use [VCR](https://github.com/myronmarston/vcr) anmo is now an option.
|
10
|
+
|
11
|
+
How?
|
12
|
+
----
|
13
|
+
|
14
|
+
```
|
15
|
+
require "anmo"
|
16
|
+
|
17
|
+
Thread.new { Anmo.launch_server }
|
18
|
+
|
19
|
+
Anmo.create_request({
|
20
|
+
:path => "/lookatmyhorse",
|
21
|
+
:body => "my horse is amazing"
|
22
|
+
})
|
23
|
+
```
|
24
|
+
|
25
|
+
```
|
26
|
+
curl http://localhost:8787/lookatmyhorse
|
27
|
+
my horse is amazing
|
28
|
+
```
|
29
|
+
|
30
|
+
Docker
|
31
|
+
----
|
32
|
+
|
33
|
+
To run using docker containers, start memcached and then start anmo.
|
34
|
+
|
35
|
+
```sh
|
36
|
+
|
37
|
+
docker run --name memcache -d memcached
|
38
|
+
|
39
|
+
docker run --link memcache:memcache -d -p 9999:9999 bbcnews/anmo
|
40
|
+
|
41
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "cucumber"
|
3
|
+
require "cucumber/rake/task"
|
4
|
+
|
5
|
+
task :default => [:test, :features]
|
6
|
+
|
7
|
+
task :test do
|
8
|
+
Dir.glob("test/**/*_test.rb").each do |f|
|
9
|
+
require File.expand_path(File.join(File.dirname(__FILE__), f))
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
task :run do
|
14
|
+
require "anmo"
|
15
|
+
Anmo.launch_server 1234
|
16
|
+
end
|
17
|
+
|
18
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
19
|
+
t.cucumber_opts = "features --format pretty"
|
20
|
+
end
|
data/anmo.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$LOAD_PATH.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "anmo/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "anmo2"
|
7
|
+
s.version = Anmo::VERSION
|
8
|
+
s.authors = ["Andrew Vos"]
|
9
|
+
s.email = ["andrew.vos@gmail.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = ""
|
12
|
+
s.description = ""
|
13
|
+
|
14
|
+
s.rubyforge_project = "anmo"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_runtime_dependency "rack"
|
22
|
+
s.add_runtime_dependency "thin", "1.6.3"
|
23
|
+
s.add_runtime_dependency "httparty"
|
24
|
+
s.add_runtime_dependency "dalli"
|
25
|
+
|
26
|
+
s.add_development_dependency "rake"
|
27
|
+
s.add_development_dependency "minitest"
|
28
|
+
s.add_development_dependency "rack-test"
|
29
|
+
s.add_development_dependency "cucumber"
|
30
|
+
s.add_development_dependency "rspec"
|
31
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
Feature: API
|
2
|
+
In order to test applications against stable APIs
|
3
|
+
As a developer
|
4
|
+
I want to be able to mock http endpoints
|
5
|
+
|
6
|
+
Scenario: Launch a server
|
7
|
+
When I execute the following code in a new thread
|
8
|
+
"""
|
9
|
+
Anmo.launch_server
|
10
|
+
"""
|
11
|
+
Then I see an anmo server on port 8787
|
12
|
+
|
13
|
+
Scenario: Launch a server with a port
|
14
|
+
When I execute the following code in a new thread
|
15
|
+
"""
|
16
|
+
Anmo.launch_server 9393
|
17
|
+
"""
|
18
|
+
Then I see an anmo server on port 9393
|
19
|
+
|
20
|
+
Scenario: Create a request
|
21
|
+
Given an anmo server
|
22
|
+
When I execute the code
|
23
|
+
"""
|
24
|
+
Anmo.server = "http://localhost:8787"
|
25
|
+
Anmo.create_request({
|
26
|
+
:path => "/lollll",
|
27
|
+
:body => "can't think of anything",
|
28
|
+
:required_headers => {"meh" => "bleh", "blah" => "blah"}
|
29
|
+
})
|
30
|
+
"""
|
31
|
+
And I request the uri "http://localhost:8787/lollll" with the headers
|
32
|
+
"""
|
33
|
+
{
|
34
|
+
"meh": "bleh",
|
35
|
+
"blah": "blah"
|
36
|
+
}
|
37
|
+
"""
|
38
|
+
Then I see the response body "can't think of anything"
|
39
|
+
|
40
|
+
Scenario: Save object
|
41
|
+
Given an anmo server
|
42
|
+
When I execute the code
|
43
|
+
"""
|
44
|
+
Anmo.server = "http://localhost:8787"
|
45
|
+
Anmo.create_request({
|
46
|
+
:path => "/some/object",
|
47
|
+
:body => "some data"
|
48
|
+
})
|
49
|
+
"""
|
50
|
+
And I request the uri "http://localhost:8787/some/object"
|
51
|
+
Then I see the response body "some data"
|
52
|
+
|
53
|
+
Scenario: Request only returns object if it has correct query parameters
|
54
|
+
Given an anmo server
|
55
|
+
When I execute the code
|
56
|
+
"""
|
57
|
+
Anmo.server = "http://localhost:8787"
|
58
|
+
Anmo.create_request({
|
59
|
+
:path => "/path?monkeys=12",
|
60
|
+
:body => "some data"
|
61
|
+
})
|
62
|
+
"""
|
63
|
+
When I request the uri "http://localhost:8787/path?monkeys=12"
|
64
|
+
Then I see the response code 200
|
65
|
+
And I see the response body "some data"
|
66
|
+
|
67
|
+
Scenario: Save object with http status code
|
68
|
+
Given an anmo server
|
69
|
+
When I execute the code
|
70
|
+
"""
|
71
|
+
Anmo.server = "http://localhost:8787"
|
72
|
+
Anmo.create_request({
|
73
|
+
:path => "/some/object",
|
74
|
+
:status => 123
|
75
|
+
})
|
76
|
+
"""
|
77
|
+
And I request the uri "http://localhost:8787/some/object"
|
78
|
+
Then I see the response code 123
|
79
|
+
|
80
|
+
Scenario: Save object with specific method
|
81
|
+
Given an anmo server
|
82
|
+
When I execute the code
|
83
|
+
"""
|
84
|
+
Anmo.server = "http://localhost:8787"
|
85
|
+
Anmo.create_request({
|
86
|
+
:path => "/some/object",
|
87
|
+
:body => "some data",
|
88
|
+
:method => :put
|
89
|
+
})
|
90
|
+
"""
|
91
|
+
And I issue a put request to the uri "http://localhost:8787/some/object"
|
92
|
+
Then I see the response body "some data"
|
93
|
+
And I issue a get request to the uri "http://localhost:8787/some/object"
|
94
|
+
Then I see the response code 404
|
95
|
+
|
96
|
+
Scenario: Request without required headers 404s
|
97
|
+
Given an anmo server
|
98
|
+
When I execute the code
|
99
|
+
"""
|
100
|
+
Anmo.server = "http://localhost:8787"
|
101
|
+
Anmo.create_request({
|
102
|
+
:path => "/some/object",
|
103
|
+
:required_headers => {:meh => "bleh", :durp => "derp"}
|
104
|
+
})
|
105
|
+
"""
|
106
|
+
And I request the uri "http://localhost:8787/some/object"
|
107
|
+
Then I see the response code 404
|
108
|
+
|
109
|
+
Scenario: Request with required headers
|
110
|
+
Given an anmo server
|
111
|
+
When I execute the code
|
112
|
+
"""
|
113
|
+
Anmo.server = "http://localhost:8787"
|
114
|
+
Anmo.create_request({
|
115
|
+
:path => "/some/object",
|
116
|
+
:body => "helloow",
|
117
|
+
:required_headers => {:meh => "bleh", :durp => "derp"}
|
118
|
+
})
|
119
|
+
"""
|
120
|
+
And I request the uri "http://localhost:8787/some/object" with the headers
|
121
|
+
"""
|
122
|
+
{
|
123
|
+
"meh": "bleh",
|
124
|
+
"durp": "derp"
|
125
|
+
}
|
126
|
+
"""
|
127
|
+
Then I see the response body "helloow"
|
128
|
+
And I see the response code 200
|
129
|
+
|
130
|
+
Scenario: Delete all saved objects
|
131
|
+
Given an anmo server
|
132
|
+
When I execute the code
|
133
|
+
"""
|
134
|
+
Anmo.server = "http://localhost:8787"
|
135
|
+
Anmo.create_request({
|
136
|
+
:path => "/some/object",
|
137
|
+
:body => "some data"
|
138
|
+
})
|
139
|
+
"""
|
140
|
+
And I execute the code
|
141
|
+
"""
|
142
|
+
Anmo.delete_all
|
143
|
+
"""
|
144
|
+
And I request the uri "http://localhost:8787/some/object"
|
145
|
+
Then I see the response code 404
|
146
|
+
|
147
|
+
Scenario: Store all requests
|
148
|
+
Given an anmo server
|
149
|
+
When I request the uri "http://localhost:8787/some/object"
|
150
|
+
Then that request should be stored
|
151
|
+
|
152
|
+
Scenario: Delete all requests
|
153
|
+
Given an anmo server
|
154
|
+
When I request the uri "http://localhost:8787/some/object"
|
155
|
+
And I execute the code
|
156
|
+
"""
|
157
|
+
Anmo.server = "http://localhost:8787"
|
158
|
+
Anmo.delete_all_requests
|
159
|
+
"""
|
160
|
+
Then there should be no stored requests
|
161
|
+
|
162
|
+
Scenario: List all saved objects
|
163
|
+
Given an anmo server
|
164
|
+
When I execute the code
|
165
|
+
"""
|
166
|
+
Anmo.server = "http://localhost:8787"
|
167
|
+
Anmo.create_request({
|
168
|
+
:path => "/some/object",
|
169
|
+
:body => "some data"
|
170
|
+
})
|
171
|
+
|
172
|
+
@result = Anmo.stored_objects
|
173
|
+
"""
|
174
|
+
Then I should see the value
|
175
|
+
"""
|
176
|
+
[
|
177
|
+
{"path" => "/some/object", "body" => "some data"}
|
178
|
+
]
|
179
|
+
"""
|
180
|
+
|
181
|
+
Scenario: Anmo knows if a server is not running
|
182
|
+
When I execute the code
|
183
|
+
"""
|
184
|
+
Anmo.server = "http://localhost:8459"
|
185
|
+
@result = Anmo.running?
|
186
|
+
"""
|
187
|
+
Then I see that the anmo server is not running
|
188
|
+
|
189
|
+
Scenario: Anmo knows if a server is running
|
190
|
+
Given an anmo server
|
191
|
+
When I execute the code
|
192
|
+
"""
|
193
|
+
Anmo.server = "http://localhost:8787"
|
194
|
+
@result = Anmo.running?
|
195
|
+
"""
|
196
|
+
Then I see that the anmo server is running
|
197
|
+
|
198
|
+
Scenario: Anmo knows what version the server is running
|
199
|
+
Given an anmo server
|
200
|
+
When I execute the code
|
201
|
+
"""
|
202
|
+
Anmo.server_version
|
203
|
+
"""
|
204
|
+
Then I see the anmo version
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require "stringio"
|
2
|
+
|
3
|
+
When /^I execute the following code in a new thread$/ do |code|
|
4
|
+
Thread.new do
|
5
|
+
eval code
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
When /^I execute the code$/ do |code|
|
10
|
+
@result = eval code
|
11
|
+
end
|
12
|
+
|
13
|
+
Then /^I see an anmo server on port (\d+)$/ do |port|
|
14
|
+
response = nil
|
15
|
+
|
16
|
+
timeout 5 do
|
17
|
+
while response.nil?
|
18
|
+
response = HTTParty.get("http://localhost:#{port}") rescue nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
response.body.should include "Not Found"
|
23
|
+
end
|
24
|
+
|
25
|
+
Given /^an anmo server$/ do
|
26
|
+
Thread.new do
|
27
|
+
Anmo.launch_server
|
28
|
+
end
|
29
|
+
|
30
|
+
timeout 5 do
|
31
|
+
response = nil
|
32
|
+
while response.nil?
|
33
|
+
response = HTTParty.get("http://localhost:8787") rescue nil
|
34
|
+
end
|
35
|
+
sleep 0.1
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
Then /^I issue a get request to the uri "([^"]*)"$/ do |uri|
|
40
|
+
@response = HTTParty.get(uri)
|
41
|
+
end
|
42
|
+
|
43
|
+
When /^I issue a put request to the uri "([^"]*)"$/ do |uri|
|
44
|
+
@response = HTTParty.put(uri)
|
45
|
+
end
|
46
|
+
|
47
|
+
When /^I request the uri "([^"]*)"$/ do |uri|
|
48
|
+
@requested_uri = uri
|
49
|
+
@response = HTTParty.get(uri)
|
50
|
+
end
|
51
|
+
|
52
|
+
When /^I request the uri "([^"]*)" with the headers$/ do |uri, headers|
|
53
|
+
headers = JSON.parse(headers)
|
54
|
+
@response = HTTParty.get(uri, :headers => headers)
|
55
|
+
end
|
56
|
+
|
57
|
+
Then /^I see the response body "([^"]*)"$/ do |body|
|
58
|
+
@response.body.should == body
|
59
|
+
end
|
60
|
+
|
61
|
+
Then /^I see the response code (\d+)$/ do |code|
|
62
|
+
@response.code.should == code.to_i
|
63
|
+
end
|
64
|
+
|
65
|
+
Then /^that request should be stored$/ do
|
66
|
+
Anmo.requests.last["PATH_INFO"].should == @requested_uri.gsub("http://localhost:8787", "")
|
67
|
+
end
|
68
|
+
|
69
|
+
Then /^there should be no stored requests$/ do
|
70
|
+
Anmo.requests.size.should == 0
|
71
|
+
end
|
72
|
+
|
73
|
+
Then /^I should see the value$/ do |code|
|
74
|
+
@result.should == eval(code)
|
75
|
+
end
|
76
|
+
|
77
|
+
Then /^I see that the anmo server is not running$/ do
|
78
|
+
@result.should == false
|
79
|
+
end
|
80
|
+
|
81
|
+
Then /^I see that the anmo server is running$/ do
|
82
|
+
@result.should == true
|
83
|
+
end
|
84
|
+
|
85
|
+
Then /^I see the anmo version$/ do
|
86
|
+
@result.should == Anmo::VERSION
|
87
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require "rack"
|
2
|
+
require "rack/test"
|
3
|
+
require "httparty"
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), "../../lib")))
|
6
|
+
require "anmo"
|
7
|
+
require "anmo/application"
|
8
|
+
|
9
|
+
World(Rack::Test::Methods)
|
10
|
+
|
11
|
+
def app
|
12
|
+
Anmo::Application.new
|
13
|
+
end
|
data/lib/anmo.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require "anmo/version"
|
2
|
+
require "anmo/application"
|
3
|
+
require "thin"
|
4
|
+
require "httparty"
|
5
|
+
require "json"
|
6
|
+
|
7
|
+
module Anmo
|
8
|
+
def self.server
|
9
|
+
@server ||= "http://localhost:#{@port}"
|
10
|
+
end
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_writer :server
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.server_version
|
17
|
+
HTTParty.get(anmo_url("__VERSION__")).body
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.launch_server(port = 8787)
|
21
|
+
@port = port
|
22
|
+
Thin::Server.start("0.0.0.0", port, Anmo::Application.new)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.create_request(options)
|
26
|
+
HTTParty.post(anmo_url("__CREATE_OBJECT__"), :body => options.to_json, :headers => { "Content-Type" => "application/json" })
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.delete_all
|
30
|
+
HTTParty.post(anmo_url("__DELETE_ALL_OBJECTS__"))
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.requests
|
34
|
+
json = HTTParty.get(anmo_url("__REQUESTS__"))
|
35
|
+
JSON.parse(json.body)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.delete_all_requests
|
39
|
+
HTTParty.post(anmo_url("__DELETE_ALL_REQUESTS__"))
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.stored_objects
|
43
|
+
json = HTTParty.get(anmo_url("__OBJECTS__"))
|
44
|
+
JSON.parse(json.body)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.running?
|
48
|
+
HTTParty.get(anmo_url("__ALIVE__")).code == 200 rescue false
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def self.anmo_url(path)
|
54
|
+
"#{server}/#{path}"
|
55
|
+
# URI.join(server, path).to_s
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,212 @@
|
|
1
|
+
require "dalli"
|
2
|
+
require "uri"
|
3
|
+
|
4
|
+
module Anmo
|
5
|
+
class ApplicationDataStore
|
6
|
+
TTL = 1_000_000
|
7
|
+
def self.objects
|
8
|
+
cached_objects = server.get("objects")
|
9
|
+
return JSON.load(cached_objects) if cached_objects
|
10
|
+
[]
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.requests
|
14
|
+
cached_requests = server.get("requests")
|
15
|
+
return JSON.load(cached_requests) if cached_requests
|
16
|
+
[]
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.insert_object(object)
|
20
|
+
all_objects = objects
|
21
|
+
all_objects.unshift(object)
|
22
|
+
server.set("objects", JSON.dump(all_objects), TTL)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.save_request(request)
|
26
|
+
all_requests = requests
|
27
|
+
all_requests << request
|
28
|
+
server.set("requests", JSON.dump(all_requests), TTL)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.clear_objects
|
32
|
+
server.delete("objects")
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.clear_requests
|
36
|
+
server.delete("requests")
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.find(path, query_string)
|
40
|
+
objects.each do |object|
|
41
|
+
object_path = object["path"].gsub(/(\?.*)$/, "")
|
42
|
+
if path == object_path
|
43
|
+
object_query_string = Rack::Utils.parse_query(object["path"].gsub(/(.*?)\?/, ""))
|
44
|
+
|
45
|
+
if query_string == object_query_string
|
46
|
+
p object
|
47
|
+
return object
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def self.server
|
56
|
+
@server ||= if ENV["MEMCACHE_SERVERS"]
|
57
|
+
Dalli::Client.new(ENV["MEMCACHE_SERVERS"],
|
58
|
+
:username => ENV["MEMCACHE_USERNAME"],
|
59
|
+
:password => ENV["MEMCACHE_PASSWORD"],
|
60
|
+
:namespace => Process.pid.to_s
|
61
|
+
)
|
62
|
+
else
|
63
|
+
Dalli::Client.new "127.0.0.1:11211", :namespace => Process.pid.to_s
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class Application
|
69
|
+
def initialize
|
70
|
+
ApplicationDataStore.clear_objects
|
71
|
+
ApplicationDataStore.clear_requests
|
72
|
+
end
|
73
|
+
|
74
|
+
def call(env)
|
75
|
+
request = Rack::Request.new(env)
|
76
|
+
|
77
|
+
if request.request_method.upcase == "OPTIONS"
|
78
|
+
return [
|
79
|
+
200,
|
80
|
+
{
|
81
|
+
"Access-Control-Allow-Origin" => "*",
|
82
|
+
"Access-Control-Allow-Methods" => "*",
|
83
|
+
"Access-Control-Allow-Headers" => "X-Requested-With,Content-Type,Authorization"
|
84
|
+
},
|
85
|
+
""
|
86
|
+
]
|
87
|
+
end
|
88
|
+
|
89
|
+
controller_methods = [
|
90
|
+
:alive,
|
91
|
+
:version,
|
92
|
+
:create_object,
|
93
|
+
:objects,
|
94
|
+
:requests,
|
95
|
+
:delete_all_objects,
|
96
|
+
:delete_all_requests
|
97
|
+
]
|
98
|
+
|
99
|
+
method = controller_methods.find { |m| request.path_info =~ /\/?__#{m.to_s.upcase}__\/?/ }
|
100
|
+
method ||= :process_normal_request
|
101
|
+
send(method, request)
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def text(text, status = 200)
|
107
|
+
[status, { "Content-Type" => "text/html", "Access-Control-Allow-Origin" => "*" }, [text]]
|
108
|
+
end
|
109
|
+
|
110
|
+
def json(json, status = 200)
|
111
|
+
[status, { "Content-Type" => "application/json", "Access-Control-Allow-Origin" => "*" }, [json]]
|
112
|
+
end
|
113
|
+
|
114
|
+
def alive(_request)
|
115
|
+
text "<h1>anmo is alive</h1>"
|
116
|
+
end
|
117
|
+
|
118
|
+
def version(_request)
|
119
|
+
text Anmo::VERSION
|
120
|
+
end
|
121
|
+
|
122
|
+
def create_object(request)
|
123
|
+
request_info = JSON.parse(read_request_body(request))
|
124
|
+
ApplicationDataStore.insert_object(request_info)
|
125
|
+
text "", 201
|
126
|
+
end
|
127
|
+
|
128
|
+
def delete_all_objects(_request)
|
129
|
+
ApplicationDataStore.clear_objects
|
130
|
+
text ""
|
131
|
+
end
|
132
|
+
|
133
|
+
def process_normal_request(request)
|
134
|
+
ApplicationDataStore.save_request(request.env)
|
135
|
+
|
136
|
+
if found_request = find_stored_request(request)
|
137
|
+
text found_request["body"], Integer(found_request["status"] || 200)
|
138
|
+
else
|
139
|
+
text "Not Found", 404
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def requests(_request)
|
144
|
+
json ApplicationDataStore.requests.to_json
|
145
|
+
end
|
146
|
+
|
147
|
+
def delete_all_requests(_request)
|
148
|
+
ApplicationDataStore.clear_requests
|
149
|
+
text ""
|
150
|
+
end
|
151
|
+
|
152
|
+
def objects(_request)
|
153
|
+
json ApplicationDataStore.objects.to_json
|
154
|
+
end
|
155
|
+
|
156
|
+
def find_stored_request(actual_request)
|
157
|
+
actual_request_query = Rack::Utils.parse_query(actual_request.query_string)
|
158
|
+
|
159
|
+
suspected_request = ApplicationDataStore.objects.find do |r|
|
160
|
+
uri = URI(r["path"])
|
161
|
+
|
162
|
+
query = Rack::Utils.parse_query(uri.query)
|
163
|
+
method = r["method"] || "GET"
|
164
|
+
|
165
|
+
if uri.query.nil?
|
166
|
+
uri.path == actual_request.path_info && method.upcase == actual_request.request_method.upcase
|
167
|
+
else
|
168
|
+
uri.path == actual_request.path_info && query == actual_request_query && method.upcase == actual_request.request_method.upcase
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
unless suspected_request.nil?
|
173
|
+
return unless request_has_required_headers(actual_request, suspected_request)
|
174
|
+
end
|
175
|
+
|
176
|
+
suspected_request
|
177
|
+
end
|
178
|
+
|
179
|
+
def request_has_same_method(initial_request, suspected_request)
|
180
|
+
return true if suspected_request["method"].nil?
|
181
|
+
suspected_request["method"].upcase == initial_request.request_method
|
182
|
+
end
|
183
|
+
|
184
|
+
def request_has_same_query(initial_request, suspected_request)
|
185
|
+
return true if suspected_request["path"].include?("?") == false
|
186
|
+
query = Rack::Utils.parse_query(suspected_request["path"].gsub(/.*\?/, ""))
|
187
|
+
query == Rack::Utils.parse_query(initial_request.query_string)
|
188
|
+
end
|
189
|
+
|
190
|
+
def request_has_required_headers(initial_request, suspected_request)
|
191
|
+
required_headers = suspected_request["required_headers"] || {}
|
192
|
+
required_headers.each do |name, value|
|
193
|
+
if initial_request.env[convert_header_name_to_rack_style_name(name)] != value
|
194
|
+
return false
|
195
|
+
end
|
196
|
+
end
|
197
|
+
true
|
198
|
+
end
|
199
|
+
|
200
|
+
def convert_header_name_to_rack_style_name(name)
|
201
|
+
name = "HTTP_#{name}"
|
202
|
+
name.gsub!("-", "_")
|
203
|
+
name.upcase!
|
204
|
+
name
|
205
|
+
end
|
206
|
+
|
207
|
+
def read_request_body(request)
|
208
|
+
request.body.rewind
|
209
|
+
request.body.read
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
data/lib/anmo/version.rb
ADDED
data/run.sh
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
require "minitest/pride"
|
2
|
+
require "minitest/autorun"
|
3
|
+
require "minitest/unit"
|
4
|
+
require "rack/test"
|
5
|
+
require "json"
|
6
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "../../lib/anmo/application"))
|
7
|
+
|
8
|
+
module Anmo
|
9
|
+
class ApplicationTest < MiniTest::Unit::TestCase
|
10
|
+
include Rack::Test::Methods
|
11
|
+
|
12
|
+
def app
|
13
|
+
Application.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def setup
|
17
|
+
ApplicationDataStore.clear_objects
|
18
|
+
ApplicationDataStore.clear_requests
|
19
|
+
end
|
20
|
+
|
21
|
+
def save_object(path, body, status, required_headers, method)
|
22
|
+
options = {
|
23
|
+
:path => path,
|
24
|
+
:body => body,
|
25
|
+
:status => status,
|
26
|
+
:required_headers => required_headers
|
27
|
+
}
|
28
|
+
options[:method] = method if method
|
29
|
+
|
30
|
+
put "__CREATE_OBJECT__", options.to_json
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_404s_if_object_does_not_exist
|
34
|
+
get "/bla/bla/bla.json"
|
35
|
+
assert_equal "Not Found", last_response.body
|
36
|
+
assert last_response.not_found?
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_stores_mock_data
|
40
|
+
save_object "/this/is/the/path.object", "please save this", nil, nil, nil
|
41
|
+
assert_equal 201, last_response.status
|
42
|
+
|
43
|
+
get "/this/is/the/path.object"
|
44
|
+
assert_equal "please save this", last_response.body
|
45
|
+
assert last_response.ok?
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_does_not_return_object_if_request_has_different_query_parameters
|
49
|
+
save_object "/path/?hello=true", "please save this", nil, nil, nil
|
50
|
+
get "/path/?hello=false"
|
51
|
+
assert_equal 404, last_response.status
|
52
|
+
assert_equal "Not Found", last_response.body
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_returns_object_if_request_has_same_query_parameters
|
56
|
+
save_object "/?hello=true&bla=bla", "please save this", nil, nil, nil
|
57
|
+
get "/?hello=true&bla=bla"
|
58
|
+
assert_equal 200, last_response.status
|
59
|
+
assert_equal "please save this", last_response.body
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_returns_object_if_request_has_query_parameters_in_different_order
|
63
|
+
save_object "/?aaa=aaa&bbb=bbb", "please save this", nil, nil, nil
|
64
|
+
get "/?bbb=bbb&aaa=aaa"
|
65
|
+
assert_equal 200, last_response.status
|
66
|
+
assert_equal "please save this", last_response.body
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_stores_status_code
|
70
|
+
save_object "/monkeys", nil, 123, nil, nil
|
71
|
+
get "/monkeys"
|
72
|
+
assert_equal 123, last_response.status
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_similar_query_string
|
76
|
+
save_object "/pulsar/assets?channel=foo.bar.baz.qux.quux.1", "Something", 200, nil, :get
|
77
|
+
save_object "/pulsar/assets?channel=foo.bar.baz.qux.quux.2", "Something else", 200, nil, :get
|
78
|
+
|
79
|
+
get "/pulsar/assets?channel=foo.bar.baz.qux.quux.1"
|
80
|
+
|
81
|
+
assert_equal 200, last_response.status
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_allows_deleting_all_objects
|
85
|
+
save_object "/this/is/the/path.object", "please save this", nil, nil, nil
|
86
|
+
|
87
|
+
get "/this/is/the/path.object"
|
88
|
+
first_response = last_response
|
89
|
+
|
90
|
+
post "__DELETE_ALL_OBJECTS__"
|
91
|
+
assert_equal 200, last_response.status
|
92
|
+
|
93
|
+
get "/this/is/the/path.object"
|
94
|
+
second_response = last_response
|
95
|
+
|
96
|
+
assert_equal "please save this", first_response.body
|
97
|
+
assert_equal "Not Found", second_response.body
|
98
|
+
assert_equal 404, second_response.status
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_404s_if_request_does_not_have_required_headers
|
102
|
+
save_object "/oh/hai", nil, nil, { "ruby" => "hipsters", "meh" => "bleh" }, nil
|
103
|
+
get "/oh/hai"
|
104
|
+
assert_equal 404, last_response.status
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_returns_value_if_request_has_required_headers
|
108
|
+
save_object "/oh/hai", "the content", nil, { "lol-ruby" => "hipsters", "meh" => "bleh" }, nil
|
109
|
+
header "lol-ruby", "hipsters"
|
110
|
+
header "meh", "bleh"
|
111
|
+
get "/oh/hai"
|
112
|
+
assert_equal 200, last_response.status
|
113
|
+
assert_equal "the content", last_response.body
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_404s_if_request_does_not_have_correct_method
|
117
|
+
save_object "/meh", "content", nil, nil, :delete
|
118
|
+
get "/meh"
|
119
|
+
assert_equal 404, last_response.status
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_returns_vaue_if_request_has_correct_method
|
123
|
+
save_object "/meh", "content", nil, nil, :delete
|
124
|
+
delete "/meh"
|
125
|
+
assert_equal 200, last_response.status
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_returns_the_last_added_object_first
|
129
|
+
save_object "/oh/hai", "the first content", nil, nil, nil
|
130
|
+
save_object "/oh/hai", "the second content", nil, nil, nil
|
131
|
+
get "/oh/hai"
|
132
|
+
assert_equal "the second content", last_response.body
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_stores_all_requests
|
136
|
+
get "/hello"
|
137
|
+
get "/hai"
|
138
|
+
get "/__REQUESTS__"
|
139
|
+
json = JSON.parse(last_response.body)
|
140
|
+
|
141
|
+
assert_equal 2, json.size
|
142
|
+
assert_equal "/hello", json.first["PATH_INFO"]
|
143
|
+
assert_equal "/hai", json.last["PATH_INFO"]
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_does_not_store_create_or_delete_requests
|
147
|
+
save_object "/oh/hai", "the first content", nil, nil, nil
|
148
|
+
post "__DELETE_ALL_OBJECTS__"
|
149
|
+
get "/__REQUESTS__"
|
150
|
+
json = JSON.parse(last_response.body)
|
151
|
+
assert_equal 0, json.size
|
152
|
+
end
|
153
|
+
|
154
|
+
def test_returns_requests_as_json
|
155
|
+
get "/hello"
|
156
|
+
get "/__REQUESTS__"
|
157
|
+
json = JSON.parse(last_response.body)
|
158
|
+
assert_equal 1, json.size
|
159
|
+
assert_equal "/hello", json.first["PATH_INFO"]
|
160
|
+
end
|
161
|
+
|
162
|
+
def test_deletes_all_requests
|
163
|
+
get "/hello"
|
164
|
+
get "/__REQUESTS__"
|
165
|
+
json = JSON.parse(last_response.body)
|
166
|
+
assert_equal 1, json.size
|
167
|
+
post "/__DELETE_ALL_REQUESTS__"
|
168
|
+
get "/__REQUESTS__"
|
169
|
+
json = JSON.parse(last_response.body)
|
170
|
+
assert_equal 0, json.size
|
171
|
+
end
|
172
|
+
|
173
|
+
def test_returns_empty_list_of_stored_objects
|
174
|
+
get "/__OBJECTS__"
|
175
|
+
json = JSON.parse(last_response.body)
|
176
|
+
assert_equal "application/json", last_response.content_type
|
177
|
+
assert_equal 0, json.size
|
178
|
+
end
|
179
|
+
|
180
|
+
def test_lists_stored_objects
|
181
|
+
save_object "/some/path", nil, nil, nil, nil
|
182
|
+
|
183
|
+
get "/__OBJECTS__"
|
184
|
+
|
185
|
+
json = JSON.parse(last_response.body)
|
186
|
+
assert_equal 1, json.size
|
187
|
+
assert_equal "/some/path", json.first["path"]
|
188
|
+
end
|
189
|
+
|
190
|
+
def test_shows_that_its_alive
|
191
|
+
get "/__ALIVE__"
|
192
|
+
assert last_response.ok?
|
193
|
+
assert_equal "<h1>anmo is alive</h1>", last_response.body
|
194
|
+
end
|
195
|
+
|
196
|
+
def test_exposes_server_version
|
197
|
+
get "/__VERSION__"
|
198
|
+
assert_equal Anmo::VERSION, last_response.body
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
metadata
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: anmo2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.31
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andrew Vos
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-07-22 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: thin
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.6.3
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.6.3
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: httparty
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
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: dalli
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: minitest
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rack-test
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: cucumber
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
description: ''
|
140
|
+
email:
|
141
|
+
- andrew.vos@gmail.com
|
142
|
+
executables: []
|
143
|
+
extensions: []
|
144
|
+
extra_rdoc_files: []
|
145
|
+
files:
|
146
|
+
- ".gitignore"
|
147
|
+
- ".rubocop.yml"
|
148
|
+
- ".ruby-version"
|
149
|
+
- ".rvmrc"
|
150
|
+
- ".travis.yml"
|
151
|
+
- Dockerfile
|
152
|
+
- Gemfile
|
153
|
+
- README.md
|
154
|
+
- Rakefile
|
155
|
+
- anmo.gemspec
|
156
|
+
- features/api.feature
|
157
|
+
- features/step_definitions/api_steps.rb
|
158
|
+
- features/support/env.rb
|
159
|
+
- lib/anmo.rb
|
160
|
+
- lib/anmo/application.rb
|
161
|
+
- lib/anmo/version.rb
|
162
|
+
- run.sh
|
163
|
+
- test/anmo/application_test.rb
|
164
|
+
homepage: ''
|
165
|
+
licenses: []
|
166
|
+
metadata: {}
|
167
|
+
post_install_message:
|
168
|
+
rdoc_options: []
|
169
|
+
require_paths:
|
170
|
+
- lib
|
171
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
172
|
+
requirements:
|
173
|
+
- - ">="
|
174
|
+
- !ruby/object:Gem::Version
|
175
|
+
version: '0'
|
176
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
requirements: []
|
182
|
+
rubyforge_project: anmo
|
183
|
+
rubygems_version: 2.4.2
|
184
|
+
signing_key:
|
185
|
+
specification_version: 4
|
186
|
+
summary: ''
|
187
|
+
test_files:
|
188
|
+
- features/api.feature
|
189
|
+
- features/step_definitions/api_steps.rb
|
190
|
+
- features/support/env.rb
|
191
|
+
- test/anmo/application_test.rb
|