riak-cache 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,35 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+ _notes
23
+ doc
24
+ .yardoc
25
+ .bundle
26
+ spec/support/test_server.yml
27
+ Gemfile*.lock
28
+ **/bin
29
+ *.rbc
30
+ .rvmrc
31
+
32
+ .riaktest
33
+ **/.riaktest
34
+ vendor/erlang
35
+ vendor/riak
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+ gem 'bundler'
5
+
6
+ if File.directory?(File.expand_path("../../riak-client", __FILE__))
7
+ gem 'riak-client', :path => "../riak-client"
8
+ end
9
+
10
+ group :guard do
11
+ gem 'guard-rspec'
12
+ gem 'rb-fsevent'
13
+ gem 'growl'
14
+ end
@@ -0,0 +1,3 @@
1
+ gem 'activesupport', '~> 3.0.11'
2
+
3
+ instance_eval File.read(File.expand_path('../Gemfile', __FILE__))
@@ -0,0 +1,3 @@
1
+ gem 'activesupport', '~> 3.1.0'
2
+
3
+ instance_eval File.read(File.expand_path('../Gemfile', __FILE__))
@@ -0,0 +1,3 @@
1
+ gem 'activesupport', '~> 3.2.0'
2
+
3
+ instance_eval File.read(File.expand_path('../Gemfile', __FILE__))
@@ -0,0 +1,15 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+ gemset = ENV['RVM_GEMSET'] || 'ripple'
4
+ gemset = "@#{gemset}" unless gemset.to_s == ''
5
+
6
+ rvms = %w[ 1.8.7 1.9.2 1.9.3 jruby ].map do |version|
7
+ "#{version}@#{gemset}"
8
+ end
9
+
10
+ guard 'rspec', :cli => '--profile', :rvm => rvms do
11
+ watch(%r{^spec/.+_spec\.rb$})
12
+ watch('spec/spec_helper.rb')
13
+ watch(%r{^lib/.+\.rb$})
14
+ end
15
+
data/LICENSE ADDED
@@ -0,0 +1,16 @@
1
+ Copyright 2010-2012 Sean Cribbs and Basho Technologies, Inc.
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
14
+
15
+ All of the files in this project are under the project-wide license
16
+ unless they are otherwise marked.
@@ -0,0 +1,59 @@
1
+ # Rails/ActiveSupport Cache Store for Riak (riak-cache) [![Build Status](https://secure.travis-ci.org/seancribbs/riak-cache.png)](http://travis-ci.org/seancribbs/riak-cache)
2
+
3
+ `riak-cache` is an implementation of `ActiveSupport::Cache::Store`
4
+ that stores cached values in Riak. This is especially useful if you
5
+ have configured Riak to use an in-memory backend.
6
+
7
+ ## Dependencies
8
+
9
+ `riak-cache` requires ActiveSupport version 3.x for its parent class,
10
+ and `riak-client` to connect to Riak.
11
+
12
+ Development dependencies are handled with bundler. Install bundler
13
+ (`gem install bundler`) and run this command:
14
+
15
+ ``` bash
16
+ $ bundle install
17
+ ```
18
+
19
+ To pin to a specific version of ActiveSupport (rather than the latest
20
+ 3.x version), specify the `BUNDLE_GEMFILE` environment variable:
21
+
22
+ ``` bash
23
+ $ BUNDLE_GEMFILE=Gemfile.rails31 bundle install
24
+ ```
25
+
26
+ ## Configuring and Using
27
+
28
+ To use `riak-cache` with your Rails project, include it in your
29
+ project's `Gemfile` and set it as the cache store inside your
30
+ `config/application.rb` like so:
31
+
32
+ ``` ruby
33
+ config.cache_store = :riak_store
34
+ ```
35
+
36
+ To provide extra configuration (for example, which Riak nodes to
37
+ connect to), use an array:
38
+
39
+ ```ruby
40
+ config.cache_store = [:riak_store, {:http_port => 10000}]
41
+ ```
42
+
43
+ Otherwise, you can simply instantiate and use the class wherever you
44
+ like:
45
+
46
+ ```ruby
47
+ cache = Riak::CacheStore.new
48
+ cache.fetch('pages/1234') { render_page }
49
+ ```
50
+
51
+ ## License & Copyright
52
+
53
+ Copyright ©2010-2012 Sean Cribbs and Basho Technologies, Inc.
54
+
55
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
56
+
57
+ [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)
58
+
59
+ Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
@@ -0,0 +1,52 @@
1
+ require 'rubygems'
2
+ require 'rubygems/package_task'
3
+ require 'rspec/core'
4
+ require 'rspec/core/rake_task'
5
+
6
+ def gemspec
7
+ $riakcache_gemspec ||= Gem::Specification.load("riak-cache.gemspec")
8
+ end
9
+
10
+ Gem::PackageTask.new(gemspec) do |pkg|
11
+ pkg.need_zip = false
12
+ pkg.need_tar = false
13
+ end
14
+
15
+ task :gem => :gemspec
16
+
17
+ desc %{Validate the gemspec file.}
18
+ task :gemspec do
19
+ gemspec.validate
20
+ end
21
+
22
+ desc %{Release the gem to RubyGems.org}
23
+ task :release => :gem do
24
+ system "gem push pkg/#{gemspec.name}-#{gemspec.version}.gem"
25
+ end
26
+
27
+ desc "Cleans up white space in source files"
28
+ task :clean_whitespace do
29
+ no_file_cleaned = true
30
+
31
+ Dir["**/*.rb"].each do |file|
32
+ contents = File.read(file)
33
+ cleaned_contents = contents.gsub(/([ \t]+)$/, '')
34
+ unless cleaned_contents == contents
35
+ no_file_cleaned = false
36
+ puts " - Cleaned #{file}"
37
+ File.open(file, 'w') { |f| f.write(cleaned_contents) }
38
+ end
39
+ end
40
+
41
+ if no_file_cleaned
42
+ puts "No files with trailing whitespace found"
43
+ end
44
+ end
45
+
46
+
47
+ desc "Run All Specs"
48
+ RSpec::Core::RakeTask.new do |spec|
49
+ spec.rspec_opts = %w[--profile]
50
+ end
51
+
52
+ task :default => :spec
@@ -0,0 +1,2 @@
1
+ require 'riak-cache'
2
+ ActiveSupport::Cache::RiakStore = Riak::CacheStore unless defined?(ActiveSupport::Cache::RiakStore)
@@ -0,0 +1,2 @@
1
+ require 'riak-cache/version'
2
+ require 'riak/cache_store'
@@ -0,0 +1,5 @@
1
+ module Riak
2
+ module Cache
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1,73 @@
1
+ require 'yaml'
2
+ require 'riak'
3
+ require 'active_support/version'
4
+ require 'active_support/cache'
5
+
6
+ module Riak
7
+ # An ActiveSupport::Cache::Store implementation that uses Riak.
8
+ # Compatible only with ActiveSupport version 3 or greater.
9
+ class CacheStore < ActiveSupport::Cache::Store
10
+ attr_accessor :client
11
+
12
+ # Creates a Riak-backed cache store.
13
+ def initialize(options = {})
14
+ super
15
+ @bucket_name = options.delete(:bucket) || '_cache'
16
+ @n_value = options.delete(:n_value) || 2
17
+ @r = options.delete(:r) || 1
18
+ @w = options.delete(:w) || 1
19
+ @dw = options.delete(:dw) || 0
20
+ @rw = options.delete(:rw) || "quorum"
21
+ @client = Riak::Client.new(options)
22
+ set_bucket_defaults
23
+ end
24
+
25
+ def bucket
26
+ @bucket ||= @client.bucket(@bucket_name)
27
+ end
28
+
29
+ def delete_matched(matcher, options={})
30
+ instrument(:delete_matched, matcher) do
31
+ bucket.keys do |keys|
32
+ keys.grep(matcher).each do |k|
33
+ bucket.delete(k)
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ protected
40
+ def set_bucket_defaults
41
+ begin
42
+ new_values = {}
43
+ new_values['n_val'] = @n_value unless bucket.n_value == @n_value
44
+ new_values['r'] = @r unless bucket.r == @r
45
+ new_values['w'] = @w unless bucket.w == @w
46
+ new_values['dw'] = @dw unless bucket.dw == @dw
47
+ new_values['rw'] = @rw unless bucket.rw == @rw
48
+ bucket.props = new_values unless new_values.empty?
49
+ rescue
50
+ end
51
+ end
52
+
53
+ def write_entry(key, value, options={})
54
+ object = bucket.get_or_new(key)
55
+ object.content_type = 'application/yaml'
56
+ object.data = value
57
+ object.store
58
+ end
59
+
60
+ def read_entry(key, options={})
61
+ begin
62
+ bucket.get(key).data
63
+ rescue Riak::FailedRequest => fr
64
+ raise fr unless fr.not_found?
65
+ nil
66
+ end
67
+ end
68
+
69
+ def delete_entry(key, options={})
70
+ bucket.delete(key)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,26 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require 'riak-cache/version'
3
+
4
+ Gem::Specification.new do |gem|
5
+ # Meta
6
+ gem.name = "riak-cache"
7
+ gem.version = Riak::Cache::VERSION
8
+ gem.summary = %Q{riak-cache is an ActiveSupport::Cache::Store that is backed by Riak.}
9
+ gem.description = %Q{riak-cache is an ActiveSupport::Cache::Store that is backed by Riak. It supports ActiveSupport version 3.x.}
10
+ gem.email = ["sean@basho.com"]
11
+ gem.homepage = "http://github.com/seancribbs/riak-cache"
12
+ gem.authors = ["Sean Cribbs"]
13
+
14
+ # Deps
15
+ gem.add_development_dependency "rspec", "~>2.8.0"
16
+ gem.add_development_dependency 'rake'
17
+ gem.add_runtime_dependency "riak-client", "~> 1.0.0"
18
+ gem.add_runtime_dependency "activesupport", "~> 3.0"
19
+
20
+ # Files
21
+ ignores = File.read(".gitignore").split(/\r?\n/).reject{ |f| f =~ /^(#.+|\s*)$/ }.map {|f| Dir[f] }.flatten
22
+ gem.files = (Dir['**/*','.gitignore'] - ignores).reject {|f| !File.file?(f) }
23
+ gem.test_files = (Dir['spec/**/*','.gitignore'] - ignores).reject {|f| !File.file?(f) }
24
+ # gem.executables = Dir['bin/*'].map { |f| File.basename(f) }
25
+ gem.require_paths = ['lib']
26
+ end
@@ -0,0 +1,127 @@
1
+ require 'spec_helper'
2
+ require 'riak/cache_store'
3
+
4
+ describe Riak::CacheStore do
5
+ let(:web_port){ test_server.http_port }
6
+ subject { ActiveSupport::Cache.lookup_store(:riak_store, :http_port => web_port) }
7
+
8
+ describe "Riak integration" do
9
+ it "should have a client" do
10
+ subject.should respond_to(:client)
11
+ subject.client.should be_kind_of(Riak::Client)
12
+ end
13
+
14
+ it "should have a bucket to store entries in" do
15
+ subject.bucket.should be_kind_of(Riak::Bucket)
16
+ end
17
+
18
+ it "should configure the client according to the initialized options" do
19
+ subject = ActiveSupport::Cache.lookup_store(:riak_store, :http_port => 10000)
20
+ subject.client.nodes.all? { |n| n.http_port == 10000 }.should == true
21
+ end
22
+
23
+ it "should choose the bucket according to the initializer option" do
24
+ cache = ActiveSupport::Cache.lookup_store(:riak_store, :bucket => "foobar", :http_port => web_port)
25
+ cache.bucket.name.should == "foobar"
26
+ end
27
+
28
+ it "should set the N value to 2 by default" do
29
+ subject.bucket.n_value.should == 2
30
+ end
31
+
32
+ it "should set the N value to the specified value" do
33
+ cache = ActiveSupport::Cache.lookup_store(:riak_store, :n_value => 1, :http_port => web_port)
34
+ cache.bucket.n_value.should == 1
35
+ end
36
+
37
+ it "should set the bucket R value to 1 by default" do
38
+ subject.bucket.r.should == 1
39
+ end
40
+
41
+ it "should set the bucket R default to the specified value" do
42
+ cache = ActiveSupport::Cache.lookup_store(:riak_store, :r => "quorum", :http_port => web_port)
43
+ cache.bucket.r.should == "quorum"
44
+ end
45
+
46
+ it "should set the bucket W value to 1 by default" do
47
+ subject.bucket.w.should == 1
48
+ end
49
+
50
+ it "should set the bucket W default to the specified value" do
51
+ cache = ActiveSupport::Cache.lookup_store(:riak_store, :w => "all", :http_port => web_port)
52
+ cache.bucket.w.should == "all"
53
+ end
54
+
55
+ it "should set the bucket DW value to 0 by default" do
56
+ subject.bucket.dw.should == 0
57
+ end
58
+
59
+ it "should set the bucket DW default to the specified value" do
60
+ cache = ActiveSupport::Cache.lookup_store(:riak_store, :dw => "quorum", :http_port => web_port)
61
+ cache.bucket.dw.should == "quorum"
62
+ end
63
+
64
+ it "should set the bucket RW value to quorum by default" do
65
+ subject.bucket.rw.should == "quorum"
66
+ end
67
+
68
+ it "should set the bucket RW default to the specified value" do
69
+ cache = ActiveSupport::Cache.lookup_store(:riak_store, :rw => "all", :http_port => web_port)
70
+ cache.bucket.rw.should == "all"
71
+ end
72
+ end
73
+
74
+
75
+ it "should read and write strings" do
76
+ subject.write('foo', 'bar')
77
+ subject.read('foo').should == 'bar'
78
+ end
79
+
80
+ it "should read and write hashes" do
81
+ subject.write('foo', {:a => "b"})
82
+ subject.read('foo').should == {:a => "b"}
83
+ end
84
+
85
+ it "should read and write integers" do
86
+ subject.write('foo', 1)
87
+ subject.read('foo').should == 1
88
+ end
89
+
90
+ it "should read and write nil" do
91
+ subject.write('foo', nil)
92
+ subject.read('foo').should be_nil
93
+ end
94
+
95
+ it "should return the stored value when fetching on hit" do
96
+ subject.write('foo', 'bar')
97
+ subject.fetch('foo'){'baz'}.should == 'bar'
98
+ end
99
+
100
+ it "should return the default value when fetching on miss" do
101
+ subject.fetch('does-not-exist'){ 'baz' }.should == 'baz'
102
+ end
103
+
104
+ it "should return the default value when forcing a miss" do
105
+ subject.fetch('foo', :force => true){'bar'}.should == 'bar'
106
+ end
107
+
108
+ it "should detect if a value exists in the cache" do
109
+ subject.write('foo', 'bar')
110
+ subject.exist?('foo').should be_true
111
+ end
112
+
113
+ it "should delete matching keys from the cache" do
114
+ subject.write('foo', 'bar')
115
+ subject.write('green', 'thumb')
116
+ subject.delete_matched(/foo/)
117
+ subject.read('foo').should be_nil
118
+ subject.read('green').should == 'thumb'
119
+ end
120
+
121
+ it "should delete a single key from the cache" do
122
+ subject.write('foo', 'bar')
123
+ subject.read('foo').should == 'bar'
124
+ subject.delete('foo')
125
+ subject.read('foo').should be_nil
126
+ end
127
+ end
@@ -0,0 +1,27 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+
4
+ require 'rubygems' # Use the gems path only for the spec suite
5
+ require 'riak'
6
+ require 'riak-cache'
7
+ require 'rspec'
8
+
9
+ # Only the tests should really get away with this.
10
+ Riak.disable_list_keys_warnings = true
11
+
12
+ %w[test_server].each do |file|
13
+ require File.join("support", file)
14
+ end
15
+
16
+ RSpec.configure do |config|
17
+ config.mock_with :rspec
18
+
19
+ config.filter_run :focus => true
20
+ config.run_all_when_everything_filtered = true
21
+
22
+ if defined?(::Java)
23
+ config.seed = Time.now.utc
24
+ else
25
+ config.order = :random
26
+ end
27
+ end
@@ -0,0 +1,57 @@
1
+ require 'riak/test_server'
2
+
3
+ module TestServerSupport
4
+ def test_server
5
+ unless $test_server
6
+ begin
7
+ require 'yaml'
8
+ config = YAML.load_file(File.expand_path("../test_server.yml", __FILE__))
9
+ $test_server = Riak::TestServer.create(:root => config['root'],
10
+ :source => config['source'],
11
+ :min_port => config['min_port'] || 15000)
12
+ rescue SocketError => e
13
+ warn "Couldn't connect to Riak TestServer! #{$test_server.inspect}"
14
+ warn "Skipping remaining integration tests."
15
+ warn_crash_log
16
+ $test_server_fatal = e
17
+ rescue => e
18
+ warn "Can't run integration specs without the test server. Please create/verify spec/support/test_server.yml."
19
+ warn "Skipping remaining integration tests."
20
+ warn e.inspect
21
+ warn_crash_log
22
+ $test_server_fatal = e
23
+ end
24
+ end
25
+ $test_server
26
+ end
27
+
28
+ def test_server_fatal
29
+ $test_server_fatal
30
+ end
31
+
32
+ def warn_crash_log
33
+ if $test_server && crash_log = $test_server.log + 'crash.log'
34
+ warn crash_log.read if crash_log.exist?
35
+ end
36
+ end
37
+ end
38
+
39
+ RSpec.configure do |config|
40
+ config.include TestServerSupport
41
+
42
+ config.before(:each) do
43
+ fail "Test server not working: #{test_server_fatal}" if test_server_fatal
44
+ test_server.create unless test_server.exist?
45
+ test_server.start
46
+ end
47
+
48
+ config.after(:each, :integration => true) do
49
+ if test_server && !test_server_fatal
50
+ test_server.drop
51
+ end
52
+ end
53
+
54
+ config.after(:suite) do
55
+ $test_server.stop if $test_server
56
+ end
57
+ end
@@ -0,0 +1,14 @@
1
+ # This is where the test server node will be generated. Something on
2
+ # /tmp is usually ok.
3
+ root: /tmp/riak-client-test-server
4
+
5
+ # This is where Riak is installed on your system, that is, the path to
6
+ # the 'riak' and 'riak-admin' scripts. I use a self-built node, but
7
+ # here's where it will generally be on various platforms:
8
+ #
9
+ # Linux: /usr/sbin
10
+ # Solaris/OpenSolaris: /opt/riak/bin
11
+ # Mac OS/X (Homebrew): /usr/local/bin
12
+ # Source/Self built: /path/to/your/install/rel/riak/bin
13
+ #
14
+ source: /Users/sean/Development/riak/rel/riak/bin
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: riak-cache
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Sean Cribbs
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &2153009100 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.8.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *2153009100
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &2153008680 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *2153008680
36
+ - !ruby/object:Gem::Dependency
37
+ name: riak-client
38
+ requirement: &2153008140 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 1.0.0
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *2153008140
47
+ - !ruby/object:Gem::Dependency
48
+ name: activesupport
49
+ requirement: &2153007640 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *2153007640
58
+ description: riak-cache is an ActiveSupport::Cache::Store that is backed by Riak.
59
+ It supports ActiveSupport version 3.x.
60
+ email:
61
+ - sean@basho.com
62
+ executables: []
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - Gemfile
67
+ - Gemfile.rails30
68
+ - Gemfile.rails31
69
+ - Gemfile.rails32
70
+ - Guardfile
71
+ - lib/active_support/cache/riak_store.rb
72
+ - lib/riak/cache_store.rb
73
+ - lib/riak-cache/version.rb
74
+ - lib/riak-cache.rb
75
+ - LICENSE
76
+ - Rakefile
77
+ - README.markdown
78
+ - riak-cache.gemspec
79
+ - spec/riak/cache_store_spec.rb
80
+ - spec/spec_helper.rb
81
+ - spec/support/test_server.rb
82
+ - spec/support/test_server.yml.example
83
+ - .gitignore
84
+ homepage: http://github.com/seancribbs/riak-cache
85
+ licenses: []
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 1.8.15
105
+ signing_key:
106
+ specification_version: 3
107
+ summary: riak-cache is an ActiveSupport::Cache::Store that is backed by Riak.
108
+ test_files:
109
+ - spec/riak/cache_store_spec.rb
110
+ - spec/spec_helper.rb
111
+ - spec/support/test_server.rb
112
+ - spec/support/test_server.yml.example
113
+ - .gitignore