baleen 0.0.2

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.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/Gemfile +6 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +29 -0
  6. data/Rakefile +1 -0
  7. data/baleen.gemspec +33 -0
  8. data/bin/baleen +17 -0
  9. data/lib/baleen/client.rb +45 -0
  10. data/lib/baleen/container.rb +49 -0
  11. data/lib/baleen/job.rb +19 -0
  12. data/lib/baleen/messages/messages.rb +48 -0
  13. data/lib/baleen/messages/request.rb +31 -0
  14. data/lib/baleen/messages/response.rb +29 -0
  15. data/lib/baleen/runner.rb +119 -0
  16. data/lib/baleen/server.rb +75 -0
  17. data/lib/baleen/task.rb +44 -0
  18. data/lib/baleen/utils/colored_puts.rb +17 -0
  19. data/lib/baleen/version.rb +3 -0
  20. data/lib/baleen.rb +5 -0
  21. data/poc/.gitignore +15 -0
  22. data/poc/Gemfile +49 -0
  23. data/poc/README.rdoc +261 -0
  24. data/poc/Rakefile +7 -0
  25. data/poc/app/assets/images/rails.png +0 -0
  26. data/poc/app/assets/javascripts/application.js +15 -0
  27. data/poc/app/assets/stylesheets/application.css +13 -0
  28. data/poc/app/controllers/application_controller.rb +3 -0
  29. data/poc/app/helpers/application_helper.rb +2 -0
  30. data/poc/app/mailers/.gitkeep +0 -0
  31. data/poc/app/models/.gitkeep +0 -0
  32. data/poc/app/models/ar_profile.rb +3 -0
  33. data/poc/app/views/layouts/application.html.erb +14 -0
  34. data/poc/config/application.rb +62 -0
  35. data/poc/config/boot.rb +6 -0
  36. data/poc/config/cucumber.yml +8 -0
  37. data/poc/config/database.yml +28 -0
  38. data/poc/config/environment.rb +5 -0
  39. data/poc/config/environments/development.rb +37 -0
  40. data/poc/config/environments/production.rb +67 -0
  41. data/poc/config/environments/test.rb +37 -0
  42. data/poc/config/initializers/backtrace_silencers.rb +7 -0
  43. data/poc/config/initializers/inflections.rb +15 -0
  44. data/poc/config/initializers/mime_types.rb +5 -0
  45. data/poc/config/initializers/secret_token.rb +7 -0
  46. data/poc/config/initializers/session_store.rb +8 -0
  47. data/poc/config/initializers/wrap_parameters.rb +14 -0
  48. data/poc/config/locales/en.yml +5 -0
  49. data/poc/config/routes.rb +58 -0
  50. data/poc/config.ru +4 -0
  51. data/poc/db/.gitkeep +0 -0
  52. data/poc/db/migrate/20130914144710_create_ar_profiles.rb +11 -0
  53. data/poc/db/schema.rb +24 -0
  54. data/poc/db/seeds.rb +7 -0
  55. data/poc/features/cpu_bound.feature +3 -0
  56. data/poc/features/io_bound.feature +3 -0
  57. data/poc/features/step_definitions/fake_test_steps.rb +25 -0
  58. data/poc/features/support/env.rb +60 -0
  59. data/poc/features/support/ruby_prof_cucumber.rb +15 -0
  60. data/poc/features/t1.feature +12 -0
  61. data/poc/features/t10.feature +12 -0
  62. data/poc/features/t2.feature +12 -0
  63. data/poc/features/t3.feature +12 -0
  64. data/poc/features/t4.feature +12 -0
  65. data/poc/features/t5.feature +12 -0
  66. data/poc/features/t6.feature +12 -0
  67. data/poc/features/t7.feature +12 -0
  68. data/poc/features/t8.feature +12 -0
  69. data/poc/features/t9.feature +12 -0
  70. data/poc/lib/assets/.gitkeep +0 -0
  71. data/poc/lib/tasks/.gitkeep +0 -0
  72. data/poc/lib/tasks/cucumber.rake +65 -0
  73. data/poc/public/404.html +26 -0
  74. data/poc/public/422.html +26 -0
  75. data/poc/public/500.html +25 -0
  76. data/poc/public/favicon.ico +0 -0
  77. data/poc/public/index.html +241 -0
  78. data/poc/public/robots.txt +5 -0
  79. data/poc/script/cucumber +10 -0
  80. data/poc/script/rails +6 -0
  81. data/poc/test/fixtures/.gitkeep +0 -0
  82. data/poc/test/fixtures/ar_profiles.yml +11 -0
  83. data/poc/test/functional/.gitkeep +0 -0
  84. data/poc/test/integration/.gitkeep +0 -0
  85. data/poc/test/performance/browsing_test.rb +12 -0
  86. data/poc/test/test_helper.rb +13 -0
  87. data/poc/test/unit/.gitkeep +0 -0
  88. data/poc/test/unit/ar_profile_test.rb +7 -0
  89. data/poc/vendor/assets/javascripts/.gitkeep +0 -0
  90. data/poc/vendor/assets/stylesheets/.gitkeep +0 -0
  91. data/poc/vendor/plugins/.gitkeep +0 -0
  92. metadata +262 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fc7921b0867d0a73836d50b562b35e7cf9692d7f
4
+ data.tar.gz: 43d2f2f70271efdd4407247e55e0341ffeb689ae
5
+ SHA512:
6
+ metadata.gz: 2c9f0dab84c29a08f66fe481fa110c26ec6a8bf06b20826077c29cc2333696d7693209ea569d0a587a899144fa5bcefb42be29b4ff2824d4b06be2e68e6d8b78
7
+ data.tar.gz: 8ec1a88eac0e64c7402f9a2414c88bb9352a08fda0e68b8fdd3c0d90064e311a331c573f1bdfe4e3550d849f133fc5fe46a80949709e718e20c811e337b7c67a
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ poc/db/.*sqlite3
19
+ poc/log/*
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in baleen.gemspec
4
+ gemspec
5
+
6
+ gem 'docker-client', path: File.expand_path("/Users/kimh/git/docker-ruby", __FILE__)
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Kim, Hirokuni
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Baleen
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'baleen'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install baleen
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/baleen.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'baleen/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "baleen"
8
+ spec.version = Baleen::VERSION
9
+ spec.authors = ["Kim, Hirokuni"]
10
+ spec.email = ["kimh@kvh.co.jp"]
11
+ spec.description = %q{Ballen allows you to run standard ruby tests in parallel and isolated environment}
12
+ spec.summary = %q{Parallel and container-based test runner powered by Docker}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "pry"
24
+ spec.add_development_dependency "awesome_print"
25
+ spec.add_development_dependency "interactive_editor"
26
+
27
+ spec.add_runtime_dependency 'celluloid'
28
+ spec.add_runtime_dependency 'celluloid-io'
29
+ spec.add_runtime_dependency 'colorize'
30
+ spec.add_runtime_dependency 'thor'
31
+
32
+ end
33
+
data/bin/baleen ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "thor"
4
+
5
+ class BaleenCommand < Thor
6
+
7
+ desc "cucumber", "Running cucumber features"
8
+ option :image, :required => true
9
+ option :work_dir, :default => "./"
10
+ option :files, :default => "features"
11
+ option :before_command, :default => nil
12
+ def cucumber
13
+ puts options[:image]
14
+ end
15
+ end
16
+
17
+ BaleenCommand.start
@@ -0,0 +1,45 @@
1
+ require 'celluloid/io'
2
+ require 'celluloid/autostart'
3
+
4
+ module Baleen
5
+ class Client
6
+ include Celluloid::IO
7
+ finalizer :close
8
+
9
+ def initialize(host, port=12345)
10
+ @socket = TCPSocket.open(host, port)
11
+ @result = "running"
12
+ async.response
13
+ end
14
+
15
+ def request(request)
16
+ @socket.puts(request.to_json)
17
+ end
18
+
19
+ def response
20
+ loop { break if handle_response(@socket.gets) }
21
+ end
22
+
23
+ def close
24
+ @socket.close if @socket
25
+ info "connection closed"
26
+
27
+ rescue IOError; nil
28
+ end
29
+
30
+ def handle_response(response)
31
+ if response.nil?
32
+ raise RuntimeError, 'Connection closed by server'
33
+ end
34
+
35
+ info "Got response"
36
+ @result = Baleen::Message::Decoder.new(response).decode
37
+ true
38
+ end
39
+
40
+ def result
41
+ @result
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,49 @@
1
+ module Baleen
2
+ module Container
3
+
4
+ class DockerClient
5
+ Result = Struct.new("Result", :status_code, :container_id, :log)
6
+
7
+ def initialize(containers)
8
+ @containers = containers
9
+ end
10
+
11
+ def create_container(params)
12
+ @container = @containers.create([params.shell, params.opt, params.commands], params.image)
13
+ end
14
+
15
+ def start_container
16
+ id = @container["Id"]
17
+
18
+ info "Start container #{id}"
19
+ @containers.start(id)
20
+ @containers.wait(id)
21
+ info "Finish container #{id}"
22
+ end
23
+
24
+ def result
25
+ id = @container["Id"]
26
+ rst = @containers.show(id)
27
+ log = @containers.logs(id)
28
+
29
+ Result.new(
30
+ rst["State"]["ExitCode"],
31
+ rst["ID"],
32
+ log
33
+ )
34
+ end
35
+
36
+ def kill_all
37
+ @containers.list.each do |container|
38
+ @containers.kill(container["Id"])
39
+ end
40
+ end
41
+
42
+ def status
43
+ info @containers.list
44
+ end
45
+
46
+ end
47
+
48
+ end
49
+ end
data/lib/baleen/job.rb ADDED
@@ -0,0 +1,19 @@
1
+ module Baleen
2
+ class Job
3
+
4
+ def initialize(client, msg)
5
+ @client = client
6
+ @msg = msg
7
+ end
8
+
9
+ def start
10
+ @client.request(@msg.params)
11
+ end
12
+
13
+ def result
14
+ @client.result
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -0,0 +1,48 @@
1
+ require "json"
2
+
3
+ module Baleen
4
+ module Message
5
+
6
+ class Decoder
7
+
8
+ def initialize(json_string)
9
+ @params = JSON.parse(json_string)
10
+ end
11
+
12
+ def decode
13
+ klass = @params.delete "klass"
14
+ Object.const_get(klass).new(symbolize_keys(@params))
15
+ end
16
+
17
+ private
18
+
19
+ def symbolize_keys(hash)
20
+ hash.inject({}){|new_hash, key_value|
21
+ key, value = key_value
22
+ value = symbolize_keys(value) if value.is_a?(Hash)
23
+ new_hash[key.to_sym] = value
24
+ new_hash
25
+ }
26
+ end
27
+ end
28
+
29
+ class Base
30
+ attr_accessor :json_msg
31
+
32
+ def initialize
33
+ @params = {}
34
+ @params[:klass] = self.class.to_s
35
+ end
36
+
37
+ def params
38
+ @params
39
+ end
40
+
41
+ def to_json
42
+ @params.to_json
43
+ end
44
+ end
45
+
46
+ end
47
+
48
+ end
@@ -0,0 +1,31 @@
1
+ require "json"
2
+
3
+ module Baleen
4
+ module Message
5
+ module Request
6
+
7
+ class ClientDisconnect < Base
8
+ def initialize(opt = {}); super() end
9
+ end
10
+
11
+ class PingPong < Base
12
+ def initialize(opt = {}); super() end
13
+ end
14
+
15
+ class Cucumber < Base
16
+ def initialize(image: "kimh/baleen-poc", work_dir: "./", files: "./features", shell: "/bin/bash", opt: "-c", exe: "bundle exec cucumber", before_command: nil, command: nil)
17
+ super()
18
+ @params[:image] = image
19
+ @params[:shell] = shell
20
+ @params[:opt] = opt
21
+ @params[:work_dir] = work_dir
22
+ @params[:files] = files
23
+ @params[:exe] = exe
24
+ @params[:before_command] = before_command
25
+ @params[:command] = command
26
+ end
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,29 @@
1
+ module Baleen
2
+ module Message
3
+ module Response
4
+
5
+ class JobComplete < Base
6
+ def initialize(opt = {})
7
+ super()
8
+ @params[:status_code] = opt[:status_code]
9
+ @params[:log ] = opt[:log]
10
+ @params[:container_id ] = opt[:log]
11
+ end
12
+
13
+ def status_code; @params[:status_code] end
14
+ def log; @params[:log] end
15
+ def container_id; @params[:container_id] end
16
+ end
17
+
18
+ class RunnerFinish < Base
19
+ def initialize(opt = {})
20
+ super()
21
+ @params[:status] = opt[:status]
22
+ end
23
+
24
+ def status; @params[:status] end
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,119 @@
1
+ module Baleen
2
+
3
+ class DockerParam
4
+ def initialize(params)
5
+ @params = params
6
+ end
7
+
8
+ def method_missing(name, *args)
9
+ DockerParam.class_eval{
10
+ define_method "#{name}" do
11
+ @params[name.to_sym]
12
+ end
13
+
14
+ define_method "#{name}=" do |*args|
15
+ @params[name.to_sym] = args.first
16
+ end
17
+ }
18
+ send(name)
19
+ end
20
+
21
+ def commands
22
+ %{
23
+ #{@params[:before_command]}
24
+ cd #{@params[:work_dir]}
25
+ #{command}
26
+ }
27
+ end
28
+
29
+ def command
30
+ @params[:command] ||= %{#{@params[:exe]} #{@params[:files]}}
31
+ end
32
+
33
+ def command=(c)
34
+ @params[:command] = c
35
+ end
36
+
37
+ def dup
38
+ copy_params = @params.dup
39
+ Object.const_get(self.class.to_s).new(copy_params)
40
+ end
41
+ end
42
+
43
+ class RunnerManager
44
+ include Celluloid::IO
45
+
46
+ CONCURRENCY=2
47
+
48
+ def initialize(containers, socket, msg)
49
+ @containers = containers
50
+ @socket = socket
51
+ @queue = []
52
+ @params = DockerParam.new(msg.params)
53
+ end
54
+
55
+ def run
56
+ create_runners.each do |runners|
57
+ @queue = runners
58
+ @queue.each do |runner|
59
+ runner.async.run
60
+ end
61
+ loop {break if monitor_runners}
62
+ end
63
+
64
+ msg = Message::Response::RunnerFinish.new(status: "done")
65
+ @socket.puts(msg.to_json)
66
+ end
67
+
68
+ private
69
+
70
+ def create_runners
71
+ target_files.map {|file|
72
+ @params.files = file
73
+ Runner.new(@containers, @params)
74
+ }.each_slice(CONCURRENCY).map {|r| r}
75
+ end
76
+
77
+ def target_files
78
+ params = @params.dup
79
+ params.command = %{find #{params.files} | grep "\\.feature"}
80
+ runner = Runner.new(@containers, params)
81
+ runner.run
82
+ runner.status.log.split("\n")
83
+ end
84
+
85
+ def monitor_runners
86
+ @queue.all?{ |r| r.status }
87
+ end
88
+ end
89
+
90
+ class Runner
91
+ include Celluloid::IO
92
+
93
+ attr_reader :status
94
+
95
+ def initialize(containers, params)
96
+ @docker_client = Container::DockerClient.new(containers)
97
+ @status = nil
98
+ @params = params
99
+ end
100
+
101
+ def run
102
+ start_runner do |result|
103
+ @status = Message::Response::JobComplete.new(
104
+ status_code: result.status_code,
105
+ container_id: result.container_id,
106
+ log: result.log
107
+ )
108
+ end
109
+ sleep 0.1 # Stop a moment until RunnerManager checks the status
110
+ end
111
+
112
+ def start_runner
113
+ @docker_client.create_container(@params)
114
+ @docker_client.start_container
115
+ yield( @docker_client.result )
116
+ end
117
+
118
+ end
119
+ end
@@ -0,0 +1,75 @@
1
+ require 'docker'
2
+ require 'awesome_print'
3
+ require 'socket'
4
+ require 'celluloid/io'
5
+ require 'celluloid/autostart'
6
+ require 'json'
7
+
8
+ module Baleen
9
+
10
+ class Server
11
+ include Celluloid::IO
12
+ finalizer :shutdown
13
+
14
+ attr_accessor :containers
15
+
16
+ def initialize(host, port: 4243)
17
+ @base_url = "http://#{host}:#{port}"
18
+ @docker = Docker::API.new(base_url: @base_url)
19
+ @containers = @docker.containers
20
+ @server = TCPServer.new("127.0.0.1", 12345)
21
+ async.run
22
+ end
23
+
24
+ def run
25
+ loop { async.handle_connection @server.accept }
26
+ end
27
+
28
+ def shutdown
29
+ @server.close if @server
30
+ end
31
+
32
+ def handle_connection(socket)
33
+ loop { handle_request(socket) }
34
+
35
+ rescue Exception => ex
36
+ case ex
37
+ when IOError; nil # when trying to close already closed socket
38
+ else
39
+ warn "Unknown exception occured"
40
+ ap ex
41
+ raise ex
42
+ end
43
+ end
44
+
45
+ def handle_request(socket)
46
+ message = socket.gets
47
+
48
+ if message.nil?
49
+ socket.close
50
+ return
51
+ end
52
+ msg = parse_request(message)
53
+
54
+ case msg
55
+ when Message::Request::PingPong
56
+ socket.puts "pong"
57
+ when Message::Request::ClientDisconnect
58
+ socket.close
59
+ when Message::Request::Cucumber
60
+ manager = RunnerManager.new(@containers, socket, msg)
61
+ manager.run
62
+ else
63
+ warn "Received unknown request"
64
+ ap msg
65
+ end
66
+ end
67
+
68
+ def parse_request(message)
69
+ #info "Got request from client"
70
+ #ap message
71
+ Baleen::Message::Decoder.new(message).decode
72
+ end
73
+ end
74
+
75
+ end
@@ -0,0 +1,44 @@
1
+ module Baleen
2
+ module Task
3
+
4
+ class Command
5
+ def initialize(work_dir, files, bundler)
6
+ @work_dir = work_dir
7
+ @files = files
8
+ @bundler = bundler
9
+ @before = []
10
+ build_default_arg
11
+ end
12
+
13
+ def before(commands=nil)
14
+ commands = sanitize_and_tokenize(commands) if commands
15
+ commands ? @before = commands : @before
16
+ end
17
+
18
+ def commands
19
+ @before ? @before + @args : @args
20
+ end
21
+
22
+ def <<(arg)
23
+ @args << arg
24
+ end
25
+
26
+ private
27
+
28
+ def build_default_arg
29
+ exe = @bundler ? "bundle exec cucumber" : "cucumber"
30
+ @args = ["cd #{@work_dir}", "#{exe} #{@files}"]
31
+ end
32
+
33
+ def sanitize_and_tokenize(arg)
34
+ # sanitize
35
+ arg = arg.strip.gsub(/^ */, '').gsub(/\n+/, "\n")
36
+ # tokenize
37
+ arg = arg.gsub(";", "\n")
38
+ arg.split("\n")
39
+ end
40
+ end
41
+
42
+ end
43
+ end
44
+
@@ -0,0 +1,17 @@
1
+ require "colorize"
2
+
3
+ module Kernel
4
+ private
5
+
6
+ def info(msg)
7
+ puts msg.green
8
+ end
9
+
10
+ def warn(msg)
11
+ puts msg.yellow
12
+ end
13
+
14
+ def error(msg)
15
+ puts msg.red
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module Baleen
2
+ VERSION = "0.0.2"
3
+ end
data/lib/baleen.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "baleen/version"
2
+
3
+ module Baleen
4
+ # Your code goes here...
5
+ end
data/poc/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ # See http://help.github.com/ignore-files/ for more about ignoring files.
2
+ #
3
+ # If you find yourself ignoring temporary files generated by your text editor
4
+ # or operating system, you probably want to add a global ignore instead:
5
+ # git config --global core.excludesfile ~/.gitignore_global
6
+
7
+ # Ignore bundler config
8
+ /.bundle
9
+
10
+ # Ignore the default SQLite database.
11
+ /db/*.sqlite3
12
+
13
+ # Ignore all logfiles and tempfiles.
14
+ /log/*.log
15
+ /tmp