mrd 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
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
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mrd.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Julien Letessier
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,39 @@
1
+ # mrd
2
+
3
+ > Stands for MySQL RAM Disk
4
+
5
+ Spawns a temporary MySQL instance off a RAM disk. Useful to speed up large test suites, your machine usable while running them, and preserve your SSD's life!
6
+
7
+ Only works under Mac OS X.
8
+
9
+ ## Installation
10
+
11
+ $ gem install mrd
12
+
13
+ ## Usage
14
+
15
+ $ mrd
16
+ ==>
17
+ Created Ramdisk at /dev/disk4
18
+ Formatted Ramdisk at /dev/disk4
19
+ Mounted Ramdisk at /Volumes/MySQLRAMDisk
20
+ Starting MySQL server
21
+ MySQL is now running.
22
+ Configure you client to use the root user, no password, and the socket at '/Volumes/MySQLRAMDisk/mysql.sock'.
23
+ Just close this terminal or press ^C when you no longer need it.
24
+
25
+ Then, if using Rails, point your `database.yml` to the temporary SQL server:
26
+
27
+ test:
28
+ ...
29
+ socket: /Volumes/MySQLRAMDisk/mysql.sock
30
+
31
+
32
+
33
+ ## Contributing
34
+
35
+ 1. Fork it
36
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
37
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
38
+ 4. Push to the branch (`git push origin my-new-feature`)
39
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/mrd ADDED
@@ -0,0 +1,259 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'logger'
4
+ require 'pathname'
5
+
6
+ require 'rubygems'
7
+ require 'term/ansicolor'
8
+
9
+ class App
10
+ class ColorLogger < ::Logger
11
+ Colors = {
12
+ 'DEBUG' => :reset,
13
+ 'INFO' => :green,
14
+ 'WARN' => :yellow,
15
+ 'ERROR' => :red,
16
+ 'FATAL' => :red,
17
+ 'UNKNOWN' => :red
18
+ }
19
+
20
+ def initialize(*args)
21
+ super
22
+ self.formatter = self.method(:custom_formatter)
23
+ end
24
+
25
+ def custom_formatter(severity, time, progname, msg)
26
+ Term::ANSIColor.send(Colors[severity], "#{msg}\n")
27
+ end
28
+ end
29
+
30
+
31
+ Logger = ColorLogger.new(STDOUT).tap do |logger|
32
+ logger.level = ColorLogger::INFO
33
+ end
34
+
35
+
36
+ module Runner
37
+ CommandError = Class.new(Exception)
38
+
39
+ def run(command)
40
+ Logger.debug("Running '#{command}'")
41
+ return %x(#{command}).strip
42
+ end
43
+
44
+ def run!(command)
45
+ run(command).tap do |result|
46
+ raise CommandError.new($?) if $? != 0
47
+ end
48
+ end
49
+ end
50
+
51
+
52
+ class Ramdisk
53
+ include Runner
54
+
55
+ def initialize(size_in_megabytes)
56
+ @size_in_megabytes = size_in_megabytes
57
+ @mountpoint = nil
58
+ @device = nil
59
+ end
60
+
61
+ def mount
62
+ @device = run!("hdid -nomount ram://#{sectors}")
63
+ Logger.info("Created Ramdisk at #{@device}")
64
+
65
+ run!("diskutil erasevolume HFS+ MySQLRAMDisk #{@device}")
66
+ Logger.info("Formatted Ramdisk at #{@device}")
67
+ Logger.info("Mounted Ramdisk at #{mountpoint}")
68
+ self
69
+ rescue Exception => e
70
+ Logger.error("Mounting failed: #{e.class.name} #{e.message}")
71
+ unmount
72
+ end
73
+
74
+ def unmount
75
+ return unless @device
76
+ Logger.info 'Ejecting RAM disk'
77
+ run!("hdiutil eject #{@device}")
78
+ @mountpoint = nil
79
+ self
80
+ rescue CommandError
81
+ run("hdiutil eject -force #{@device}")
82
+ self
83
+ end
84
+
85
+
86
+ def mountpoint
87
+ @mountpoint ||= begin
88
+ mounts = run!("mount")
89
+ mounts =~ /#{@device} on (.*) \(/
90
+ $1 or raise RuntimeError.new("Cannot determine mountpoint")
91
+ Pathname.new($1)
92
+ end
93
+ end
94
+
95
+ private
96
+
97
+ def sectors
98
+ @size_in_megabytes * 1024*1024/512
99
+ end
100
+ end
101
+
102
+
103
+ class MySQLServer
104
+ include Runner
105
+
106
+ attr :socket
107
+
108
+ def initialize(ramdisk)
109
+ @ramdisk = ramdisk
110
+ @socket = @ramdisk.mountpoint.join('mysql.sock')
111
+ @pidfile = @ramdisk.mountpoint.join('mysql.pid')
112
+ @config = @ramdisk.mountpoint.join('my.cnf')
113
+
114
+ @install_db = find_command! %w(mysql_install_db5 mysql_install_db)
115
+ @mysqld = find_command! %w(mysqld_safe5 mysqld_safe)
116
+ end
117
+
118
+ def start
119
+ # Setup config
120
+ File.open(@config, 'w') do |fd|
121
+ fd.write %Q{
122
+ [client]
123
+ socket = #{@socket}
124
+ [mysqld]
125
+ socket = #{@socket}
126
+ thread_concurrency = 4
127
+ # innodb_data_home_dir =
128
+ innodb_data_file_path = ibdata1:10M:autoextend
129
+ #innodb_log_group_home_dir = /opt/local/var/db/mysql5
130
+ innodb_file_per_table
131
+ ft_min_word_len = 3
132
+ }.gsub(/^\s+/,'')
133
+ end
134
+
135
+ # Setup base databases
136
+ run!("#{@install_db} --datadir='#{@ramdisk.mountpoint}'")
137
+
138
+ # Start server in another thread
139
+ Logger.info 'Starting MySQL server'
140
+ @thread = Thread.new do
141
+ run!("#{@mysqld} --defaults-file='#{@config}' --socket='#{@socket}' --datadir='#{@ramdisk.mountpoint}' --pid-file='#{@pidfile}' --skip-networking")
142
+ end
143
+
144
+ # Wait for PID file
145
+ wait_for { Logger.debug "Waiting for PID file..." ; @pidfile.exist? }
146
+ self
147
+ end
148
+
149
+ def stop
150
+ return unless @pidfile
151
+ # Terminate server
152
+ Logger.info "Stopping server..."
153
+ pid = @pidfile.read.to_i
154
+ Process.kill("TERM", pid)
155
+ Logger.debug "Killed server process (#{pid})."
156
+ wait_for { Logger.debug "Waiting for PID file..." ; !@pidfile.exist? }
157
+ end
158
+
159
+ private
160
+
161
+ def wait_for(options = {})
162
+ tries = options.fetch(:tries, 10)
163
+ delay = options.fetch(:delay, 1.0)
164
+ 1.upto(tries) do |index|
165
+ break if yield
166
+ Kernel.sleep(delay)
167
+ end
168
+ yield
169
+ end
170
+
171
+ def find_command!(variants)
172
+ variants.each do |variant|
173
+ path = run("which #{variant}")
174
+ return path unless path.empty?
175
+ end
176
+ Logger.error "Sorry, I need one of #{variants.join ', '} to run."
177
+ Kernel.exit
178
+ end
179
+ end
180
+
181
+
182
+ def initialize(argv)
183
+ @options = OpenStruct.new(:verbose => false, :size => 1024)
184
+ option_parser.parse(argv)
185
+
186
+ Logger.level = options.verbose ?
187
+ ColorLogger::DEBUG :
188
+ ColorLogger::INFO
189
+ end
190
+
191
+
192
+ def main
193
+ if pid = Process.fork
194
+ parent_main(pid)
195
+ else
196
+ child_main
197
+ end
198
+ end
199
+
200
+ def parent_main(child_pid)
201
+ # Logger.info 'in parent'
202
+ Process.detach(child_pid)
203
+ wait_forever
204
+ rescue Interrupt
205
+ Kernel.exit
206
+ end
207
+
208
+ def child_main
209
+ # Logger.info 'in child'
210
+ @ramdisk = Ramdisk.new(options.size).mount
211
+ @server = MySQLServer.new(@ramdisk).start
212
+ Signal.trap("HUP") do
213
+ Logger.warn "SIGHUP received"
214
+ cleanup
215
+ end
216
+
217
+ # Log and wait for user input
218
+ Logger.info "MySQL is now running."
219
+ Logger.info "Configure you client to use the root user, no password, and the socket at '#{@server.socket}'."
220
+ Logger.info "Just close this terminal or press ^C when you no longer need it."
221
+ wait_forever
222
+
223
+ rescue Interrupt, SystemExit
224
+ Logger.warn 'Session terminated.'
225
+ ensure
226
+ cleanup
227
+ end
228
+
229
+ def cleanup
230
+ @server.stop if @server
231
+ @ramdisk.unmount if @ramdisk
232
+ end
233
+
234
+
235
+ private
236
+
237
+ attr :options
238
+
239
+ def option_parser
240
+ OptionParser.new do |opts|
241
+ opts.banner = "Usage: #{$progname} [options]"
242
+
243
+ opts.on("-v", "--verbose", "Run verbosely") do |v|
244
+ options.verbose = true
245
+ end
246
+
247
+ opts.on("-s", "--size SIZE", Integer, "Size of RAM disk in MB (default 1024)") do |v|
248
+ options.size = v
249
+ end
250
+ end
251
+ end
252
+
253
+ def wait_forever
254
+ Kernel.sleep 1_000_000
255
+ end
256
+ end
257
+
258
+
259
+ App.new(ARGV).main
@@ -0,0 +1,3 @@
1
+ module Mrd
2
+ VERSION = "0.0.1"
3
+ end
data/lib/mrd.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "mrd/version"
2
+
3
+ module Mrd
4
+ # Your code goes here...
5
+ end
data/mrd.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mrd/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "mrd"
8
+ gem.version = Mrd::VERSION
9
+ gem.authors = ["Julien Letessier"]
10
+ gem.email = ["julien.letessier@gmail.com"]
11
+ gem.description = %q{Under Darwin, spawns a temporary MySQL instance off a RAM disk. Useful to speed up large test suites, your machine usable while running them, and preserve your SSD's life!}
12
+ gem.summary = %q{Run MySQL off a RAM disk}
13
+ gem.homepage = ""
14
+
15
+ gem.add_development_dependency "rake"
16
+ gem.add_dependency "term-ansicolor"
17
+
18
+ gem.files = `git ls-files`.split($/)
19
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
20
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
21
+ gem.require_paths = ["lib"]
22
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mrd
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Julien Letessier
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: term-ansicolor
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: Under Darwin, spawns a temporary MySQL instance off a RAM disk. Useful
47
+ to speed up large test suites, your machine usable while running them, and preserve
48
+ your SSD's life!
49
+ email:
50
+ - julien.letessier@gmail.com
51
+ executables:
52
+ - mrd
53
+ extensions: []
54
+ extra_rdoc_files: []
55
+ files:
56
+ - .gitignore
57
+ - Gemfile
58
+ - LICENSE.txt
59
+ - README.md
60
+ - Rakefile
61
+ - bin/mrd
62
+ - lib/mrd.rb
63
+ - lib/mrd/version.rb
64
+ - mrd.gemspec
65
+ homepage: ''
66
+ licenses: []
67
+ post_install_message:
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ segments:
78
+ - 0
79
+ hash: -335233365963778714
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ segments:
87
+ - 0
88
+ hash: -335233365963778714
89
+ requirements: []
90
+ rubyforge_project:
91
+ rubygems_version: 1.8.23
92
+ signing_key:
93
+ specification_version: 3
94
+ summary: Run MySQL off a RAM disk
95
+ test_files: []