mrd 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 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: []