dm-dh_api-adapter 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +5 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/README +7 -0
- data/Rakefile +2 -0
- data/dm-dh_api-adapter.gemspec +24 -0
- data/lib/dm-dh_api-adapter.rb +113 -0
- data/lib/dm-dh_api-adapter/models.rb +49 -0
- data/lib/dm-dh_api-adapter/version.rb +7 -0
- data/spec/setup_spec.rb +197 -0
- data/testing.rb +31 -0
- metadata +125 -0
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm 1.8.7@dm-dh_api-adapter --create
|
data/Gemfile
ADDED
data/README
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "dm-dh_api-adapter/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "dm-dh_api-adapter"
|
7
|
+
s.version = Dm::Dhapi::Adapter::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Rui Andrada"]
|
10
|
+
s.email = ["shingonoide@gmail.com"]
|
11
|
+
s.homepage = "http://gihub.com/shingonoide/dm-dh_api-adapter"
|
12
|
+
s.summary = %q{DataMapper adapter for access Dreamhost's API}
|
13
|
+
s.description = %q{Simple DataMapper adapter for access Dreamhost's API, just read for now.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "dm-dh_api-adapter"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
s.add_runtime_dependency("dm-core", "~> 1.1.0")
|
22
|
+
s.add_runtime_dependency("httparty", "~> 0.7.4")
|
23
|
+
s.add_development_dependency("rspec", "~> 1.3.1")
|
24
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'dm-core'
|
3
|
+
|
4
|
+
module DataMapper
|
5
|
+
module DhApi
|
6
|
+
module Adapter
|
7
|
+
include HTTParty
|
8
|
+
format :json
|
9
|
+
base_uri 'https://api.dreamhost.com'
|
10
|
+
|
11
|
+
def self.api_key(api_key)
|
12
|
+
@@api_key = api_key
|
13
|
+
end
|
14
|
+
def self.domains
|
15
|
+
options = {:query => {:key => @@api_key, :format => 'json', :cmd => 'domain-list_domains'}}
|
16
|
+
request_api(options)
|
17
|
+
end
|
18
|
+
def self.dnses
|
19
|
+
options = {:query => {:key => @@api_key, :format => 'json', :cmd => 'dns-list_records'}}
|
20
|
+
request_api(options)
|
21
|
+
end
|
22
|
+
def self.users
|
23
|
+
options = {:query => {:key => @@api_key, :format => 'json', :cmd => 'user-list_users_no_pw'}}
|
24
|
+
request_api(options)
|
25
|
+
end
|
26
|
+
def self.users_pw
|
27
|
+
options = {:query => {:key => @@api_key, :format => 'json', :cmd => 'user-list_users'}}
|
28
|
+
request_api(options)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.request_api(options)
|
32
|
+
response = get('/', options)
|
33
|
+
case response['result']
|
34
|
+
when 'success'
|
35
|
+
response['data']
|
36
|
+
when 'error'
|
37
|
+
raise APIRequestError, "Error: #{response['data']}" + (response['reason'].blank? ? " " : " - Reason: #{response['reason']}")
|
38
|
+
else
|
39
|
+
raise APIRequestError, "Unknown exception"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class APIRequestError < RuntimeError; end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module DataMapper
|
49
|
+
module Adapters
|
50
|
+
class DhApiAdapter < AbstractAdapter
|
51
|
+
|
52
|
+
# Looks up one record or a collection of records from the data-store:
|
53
|
+
# "SELECT" in SQL.
|
54
|
+
#
|
55
|
+
# @param [Query] query
|
56
|
+
# The query to be used to seach for the resources
|
57
|
+
#
|
58
|
+
# @return [Array]
|
59
|
+
# An Array of Hashes containing the key-value pairs for
|
60
|
+
# each record
|
61
|
+
#
|
62
|
+
# @api semipublic
|
63
|
+
def read(query)
|
64
|
+
query.filter_records(records_for(query.model).dup)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
# Make a new instance of the adapter. The @records ivar is the 'data-store'
|
70
|
+
# for this adapter. It is not shared amongst multiple incarnations of this
|
71
|
+
# adapter, eg DataMapper.setup(:default, :adapter => :in_memory);
|
72
|
+
# DataMapper.setup(:alternate, :adapter => :in_memory) do not share the
|
73
|
+
# data-store between them.
|
74
|
+
#
|
75
|
+
# @param [String, Symbol] name
|
76
|
+
# The name of the Repository using this adapter.
|
77
|
+
# @param [String, Hash] uri_or_options
|
78
|
+
# The connection uri string, or a hash of options to set up
|
79
|
+
# the adapter
|
80
|
+
#
|
81
|
+
# @api semipublic
|
82
|
+
def initialize(name, options = {})
|
83
|
+
super
|
84
|
+
DataMapper::DhApi::Adapter.api_key(options.delete(:api_key))
|
85
|
+
@records = {}
|
86
|
+
end
|
87
|
+
|
88
|
+
# All the records we're storing. This method will look them up by model name
|
89
|
+
#
|
90
|
+
# @api private
|
91
|
+
def records_for(model)
|
92
|
+
storage_name = model.storage_name(name)
|
93
|
+
case storage_name.to_sym
|
94
|
+
when :domains, :domain, :data_mapper_dh_api_models_domains
|
95
|
+
@records[storage_name] ||= DataMapper::DhApi::Adapter.domains
|
96
|
+
when :dnses, :dns, :data_mapper_dh_api_models_dns
|
97
|
+
@records[storage_name] ||= DataMapper::DhApi::Adapter.dnses
|
98
|
+
when :users, :user, :data_mapper_dh_api_models_users
|
99
|
+
@records[storage_name] ||= DataMapper::DhApi::Adapter.users
|
100
|
+
when :users_pw, :user_pw, :data_mapper_dh_api_models_users_pw
|
101
|
+
@records[storage_name] ||= DataMapper::DhApi::Adapter.users_pw
|
102
|
+
else
|
103
|
+
raise NotImplementedError, "Storage (#{storage_name}) not implemented"
|
104
|
+
end
|
105
|
+
|
106
|
+
@records[storage_name] ||= []
|
107
|
+
end
|
108
|
+
|
109
|
+
end # class DhApiAdapter
|
110
|
+
|
111
|
+
const_added(:DhApiAdapter)
|
112
|
+
end # module Adapters
|
113
|
+
end # module DataMapper
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module DhApi
|
3
|
+
module Models
|
4
|
+
class DNS
|
5
|
+
include DataMapper::Resource
|
6
|
+
|
7
|
+
property :comment, String
|
8
|
+
property :zone, String
|
9
|
+
property :type, String
|
10
|
+
property :editable, Boolean
|
11
|
+
property :value, String
|
12
|
+
property :record, String
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
class Domain
|
17
|
+
include DataMapper::Resource
|
18
|
+
property :fastcgi, Boolean
|
19
|
+
property :hosting_type, String
|
20
|
+
property :xcache, Boolean
|
21
|
+
property :php_fcgid, Boolean
|
22
|
+
property :security, Boolean
|
23
|
+
property :unique_ip, String
|
24
|
+
property :domain, String
|
25
|
+
property :account, String
|
26
|
+
property :type, String
|
27
|
+
property :passanger, Boolean
|
28
|
+
property :path, String
|
29
|
+
property :home, String
|
30
|
+
property :user, String
|
31
|
+
property :www_or_not, String
|
32
|
+
end
|
33
|
+
|
34
|
+
class User
|
35
|
+
include DataMapper::Resource
|
36
|
+
|
37
|
+
property :disk_used_mb, Float
|
38
|
+
property :gecos, String
|
39
|
+
property :username, String
|
40
|
+
property :account, String
|
41
|
+
property :type, String
|
42
|
+
property :quota_mb, String
|
43
|
+
property :shell, String
|
44
|
+
property :home, String
|
45
|
+
property :password, String
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/spec/setup_spec.rb
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'dm-core/spec/setup'
|
2
|
+
require 'dm-dh_api-adapter/models'
|
3
|
+
|
4
|
+
describe DataMapper do
|
5
|
+
describe '.setup' do
|
6
|
+
describe 'using connection string' do
|
7
|
+
before :all do
|
8
|
+
@return = DataMapper.setup(:setup_test, 'dh_api:///?api_key=6SHU5P2HLDAYECUM&foo=bar&baz=foo#fragment')
|
9
|
+
|
10
|
+
@options = @return.options
|
11
|
+
end
|
12
|
+
|
13
|
+
after :all do
|
14
|
+
DataMapper::Repository.adapters.delete(@return.name)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should return an Adapter DhApi' do
|
18
|
+
@return.should be_kind_of(DataMapper::Adapters::DhApiAdapter)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should set up the repository' do
|
22
|
+
DataMapper.repository(:setup_test).adapter.should equal(@return)
|
23
|
+
end
|
24
|
+
|
25
|
+
{
|
26
|
+
:adapter => 'dh_api',
|
27
|
+
:query => 'api_key=6SHU5P2HLDAYECUM&foo=bar&baz=foo'
|
28
|
+
}.each do |key, val|
|
29
|
+
it "should extract the #{key.inspect} option from the uri" do
|
30
|
+
@options[key].should == val
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should alias the scheme of the uri as the adapter' do
|
35
|
+
@options[:scheme].should == @options[:adapter]
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should leave the query param intact' do
|
39
|
+
@options[:query].should == 'api_key=6SHU5P2HLDAYECUM&foo=bar&baz=foo'
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should extract the query param as top-level options' do
|
43
|
+
@options[:api_key].should == '6SHU5P2HLDAYECUM'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'using options' do
|
48
|
+
before :all do
|
49
|
+
@return = DataMapper.setup(:setup_test, :adapter => :dh_api, :api_key => '6SHU5P2HLDAYECUM')
|
50
|
+
|
51
|
+
@options = @return.options
|
52
|
+
end
|
53
|
+
|
54
|
+
after :all do
|
55
|
+
DataMapper::Repository.adapters.delete(@return.name)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should return an Adapter' do
|
59
|
+
@return.should be_kind_of(DataMapper::Adapters::DhApiAdapter)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should set up the repository' do
|
63
|
+
DataMapper.repository(:setup_test).adapter.should equal(@return)
|
64
|
+
end
|
65
|
+
|
66
|
+
{
|
67
|
+
:adapter => :dh_api,
|
68
|
+
:api_key => '6SHU5P2HLDAYECUM'
|
69
|
+
}.each do |key, val|
|
70
|
+
it "should set the #{key.inspect} option" do
|
71
|
+
@options[key].should == val
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe 'using invalid options' do
|
77
|
+
it 'should raise an exception' do
|
78
|
+
lambda {
|
79
|
+
DataMapper.setup(:setup_test, :invalid)
|
80
|
+
}.should raise_error(ArgumentError, '+options+ should be Hash or Addressable::URI or String, but was Symbol')
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe 'using an instance of an adapter' do
|
85
|
+
before :all do
|
86
|
+
@adapter = DataMapper::Adapters::DhApiAdapter.new(:setup_test)
|
87
|
+
|
88
|
+
@return = DataMapper.setup(@adapter)
|
89
|
+
end
|
90
|
+
|
91
|
+
after :all do
|
92
|
+
DataMapper::Repository.adapters.delete(@return.name)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'should return an Adapter' do
|
96
|
+
@return.should be_kind_of(DataMapper::Adapters::DhApiAdapter)
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should set up the repository' do
|
100
|
+
DataMapper.repository(:setup_test).adapter.should equal(@return)
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should use the adapter given' do
|
104
|
+
@return.should == @adapter
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'should use the name given to the adapter' do
|
108
|
+
@return.name.should == @adapter.name
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "Dh_Api adapter builtin " do
|
115
|
+
|
116
|
+
before :all do
|
117
|
+
@return = DataMapper.setup(:default, :adapter => :dh_api, :api_key => '6SHU5P2HLDAYECUM')
|
118
|
+
|
119
|
+
@options = @return.options
|
120
|
+
end
|
121
|
+
|
122
|
+
after :all do
|
123
|
+
DataMapper::Repository.adapters.delete(@return.name)
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "Model for Domain" do
|
127
|
+
|
128
|
+
it "should first record exists" do
|
129
|
+
DataMapper::DhApi::Models::Domain.first.should be
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should first.domain record be 718apts.com" do
|
133
|
+
DataMapper::DhApi::Models::Domain.first.domain.should == '718apts.com'
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should last.domain record be 718apts.com" do
|
137
|
+
DataMapper::DhApi::Models::Domain.last.domain.should == '718apts.com'
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should get Domain.all records and count 14 domains" do
|
141
|
+
domains = DataMapper::DhApi::Models::Domain.all
|
142
|
+
domains.count.should be(14)
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
describe "Model for DNS" do
|
148
|
+
|
149
|
+
it "should first record exists" do
|
150
|
+
DataMapper::DhApi::Models::DNS.first.should be
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should first.record be 718apts.com" do
|
154
|
+
DataMapper::DhApi::Models::DNS.first.record.should == '718apts.com'
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should last.record be 718apts.com" do
|
158
|
+
DataMapper::DhApi::Models::DNS.last.record.should == '718apts.com'
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should get all and count 202 records" do
|
162
|
+
DataMapper::DhApi::Models::DNS.all.count.should be(202)
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
describe "Model for User" do
|
168
|
+
it "should first record exists" do
|
169
|
+
DataMapper::DhApi::Models::User.first.should be
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should first.username be apts718" do
|
173
|
+
DataMapper::DhApi::Models::User.first.username.should == 'apts718'
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should last.username be apts718" do
|
177
|
+
DataMapper::DhApi::Models::User.last.username.should == 'apts718'
|
178
|
+
end
|
179
|
+
|
180
|
+
it "should get all and count 15 records" do
|
181
|
+
DataMapper::DhApi::Models::User.all.count.should be(15)
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
describe 'DataMapper::Adapters::DhApiAdapter' do
|
188
|
+
|
189
|
+
before :all do
|
190
|
+
@return = DataMapper.setup(:default, :adapter => :dh_api, :api_key => '6SHU5P2HLDAYECUM')
|
191
|
+
|
192
|
+
@options = @return.options
|
193
|
+
@repository = DataMapper.repository(@return.name)
|
194
|
+
end
|
195
|
+
|
196
|
+
|
197
|
+
end
|
data/testing.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'dm-core'
|
3
|
+
#require 'dm-types'
|
4
|
+
require 'dm-dh_api-adapter'
|
5
|
+
|
6
|
+
DataMapper.setup(:default, :adapter => :dh_api, :api_key => '6SHU5P2HLDAYECUM')
|
7
|
+
|
8
|
+
require 'dm-dh_api-adapter/models'
|
9
|
+
require 'pp'
|
10
|
+
include DataMapper::DhApi::Models
|
11
|
+
|
12
|
+
domains = Domain.all
|
13
|
+
dnses_editables = DNS.all(:editable => true)
|
14
|
+
dnses_not_editables = DNS.all(:editable => false)
|
15
|
+
users = User.all
|
16
|
+
pp Domain.all
|
17
|
+
|
18
|
+
puts "#{Domain.last.domain}"
|
19
|
+
puts "There's #{domains.size} domains records"
|
20
|
+
|
21
|
+
puts "There's #{dnses_editables.size} editables dnses records"
|
22
|
+
puts "There's #{dnses_not_editables.size} not editables dnses records"
|
23
|
+
|
24
|
+
puts "There's #{users.size} users records"
|
25
|
+
|
26
|
+
|
27
|
+
domains[0].domain = "testing.com"
|
28
|
+
|
29
|
+
puts domains[0].domain
|
30
|
+
|
31
|
+
domains.save
|
metadata
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dm-dh_api-adapter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Rui Andrada
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-03-25 00:00:00 -03:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: dm-core
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 19
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 1
|
33
|
+
- 0
|
34
|
+
version: 1.1.0
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: httparty
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 11
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
- 7
|
49
|
+
- 4
|
50
|
+
version: 0.7.4
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: rspec
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 25
|
62
|
+
segments:
|
63
|
+
- 1
|
64
|
+
- 3
|
65
|
+
- 1
|
66
|
+
version: 1.3.1
|
67
|
+
type: :development
|
68
|
+
version_requirements: *id003
|
69
|
+
description: Simple DataMapper adapter for access Dreamhost's API, just read for now.
|
70
|
+
email:
|
71
|
+
- shingonoide@gmail.com
|
72
|
+
executables: []
|
73
|
+
|
74
|
+
extensions: []
|
75
|
+
|
76
|
+
extra_rdoc_files: []
|
77
|
+
|
78
|
+
files:
|
79
|
+
- .gitignore
|
80
|
+
- .rvmrc
|
81
|
+
- Gemfile
|
82
|
+
- README
|
83
|
+
- Rakefile
|
84
|
+
- dm-dh_api-adapter.gemspec
|
85
|
+
- lib/dm-dh_api-adapter.rb
|
86
|
+
- lib/dm-dh_api-adapter/models.rb
|
87
|
+
- lib/dm-dh_api-adapter/version.rb
|
88
|
+
- spec/setup_spec.rb
|
89
|
+
- testing.rb
|
90
|
+
has_rdoc: true
|
91
|
+
homepage: http://gihub.com/shingonoide/dm-dh_api-adapter
|
92
|
+
licenses: []
|
93
|
+
|
94
|
+
post_install_message:
|
95
|
+
rdoc_options: []
|
96
|
+
|
97
|
+
require_paths:
|
98
|
+
- lib
|
99
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
100
|
+
none: false
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
hash: 3
|
105
|
+
segments:
|
106
|
+
- 0
|
107
|
+
version: "0"
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
hash: 3
|
114
|
+
segments:
|
115
|
+
- 0
|
116
|
+
version: "0"
|
117
|
+
requirements: []
|
118
|
+
|
119
|
+
rubyforge_project: dm-dh_api-adapter
|
120
|
+
rubygems_version: 1.6.2
|
121
|
+
signing_key:
|
122
|
+
specification_version: 3
|
123
|
+
summary: DataMapper adapter for access Dreamhost's API
|
124
|
+
test_files:
|
125
|
+
- spec/setup_spec.rb
|