mr-sparkle 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +45 -0
- data/Rakefile +1 -0
- data/bin/mr-sparkle +19 -0
- data/lib/mr-sparkle.rb +70 -0
- data/lib/mr-sparkle/unicorn.conf.rb +8 -0
- data/lib/mr-sparkle/version.rb +5 -0
- data/mr-sparkle.gemspec +28 -0
- metadata +103 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Micah Chalmer
|
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,45 @@
|
|
1
|
+
# Mr. Sparkle - The Magical Reloading Unicorn
|
2
|
+
|
3
|
+
This gem contains a script to start a [Unicorn](http://unicorn.bogomips.org/)-based server for your [Rack](http://rack.github.com/) application that reloads your automatically when they are changed, but doesn't incur the penalty of reloading all the gem dependencies. It's based on Jonathan D. Stott's blog post ["Magical Reloading Sparkles"](http://namelessjon.posterous.com/magical-reloading-sparkles)--hence the name. (The name is also a Simpsons reference. [CAN YOU SEE THAT I AM SERIOUS?](http://www.youtube.com/watch?v=dnaLRbbc-54))
|
4
|
+
|
5
|
+
The main purpose of this gem is to take Jonathan's idea and package it into something can "just work" without having to be customized inside each project's code. Besides the gem packaging, this code differs from the original watcher script in the following ways:
|
6
|
+
|
7
|
+
1. It uses [listen](https://github.com/guard/listen) instead of [directory_watcher](https://github.com/TwP/directory_watcher/), which provides native event-driven file change detection, instead of polling.
|
8
|
+
1. It assumes you're using [Bundler](http://gembundler.com/) for dependencies, which means that instead of needing a hardcoded list of gems in the `before_fork` hook, like the blog post had, this plugin just does `Bundler.require(:default)` to get all the modules mentioned in the Gemfile loaded before forking.
|
9
|
+
1. If you change your Gemfile, the preloads are no longer valid, so this script treats that change as a special case: when the Gemfile changes, we kill the whole server and restart it, thus reloading absolutely everything.
|
10
|
+
|
11
|
+
The script comes with a default set of file extensions it will watch for changes. I've tried to be liberal about it--no harm reloading a few extra times when developing. You can run `mr-sparkle --help` to see the default set as a regexp, and you can change that regexp with the `--pattern` option.
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
$ gem install mr-sparkle
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
If you've got a Rack app that uses Bundler for its dependencies, then ordinarily all you have to do is execute
|
20
|
+
|
21
|
+
$ mr-sparkle
|
22
|
+
|
23
|
+
in your project's root directory. You'll get a server listening on port 8080 (bound to all addresses, so external machines WILL be able to connect to it by default) and your code will be reloaded if any relevant files change.
|
24
|
+
|
25
|
+
You can use command-line options to change the behavior a bit as follows:
|
26
|
+
|
27
|
+
$ mr-sparkle [--pattern regex] [--full-reload-pattern regex] [-- [unicorn options]]
|
28
|
+
|
29
|
+
Use `--pattern` to replace the default regex that files must match to trigger a reload. I've tried to make the default fairly liberal--it includes all extensions registered with [tilt](https://github.com/rtomayko/tilt/), for instance--so for most apps it will probably work fine.
|
30
|
+
|
31
|
+
Use `--full-reload-pattern` to trigger a full reload for a different set of files. By default it only does this for `Gemfile.`
|
32
|
+
|
33
|
+
Any arguments after the `--` will be passed on to unicorn. This is how you would change the default port, make it not bind to external ip addresses, use a rackup file with a name other than `config.ru`, etc. See [the unicorn documentation](http://unicorn.bogomips.org/unicorn_1.html) for exactly what you can pass here. Do not pass the `-c` option to unicorn--`mr-sparkle` comes with its own unicorn config file that it will use automatically.
|
34
|
+
|
35
|
+
## Requirements
|
36
|
+
|
37
|
+
This script requires Ruby 1.9.1 or greater (because it depends on `Kernel.spawn`.) Since it's a wrapper around Unicorn, it will only work on "unix or unix-like" systems where unicorn is supported.
|
38
|
+
|
39
|
+
## Contributing
|
40
|
+
|
41
|
+
1. Fork it
|
42
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
43
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
44
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
45
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/mr-sparkle
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require_relative '../lib/mr-sparkle'
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
options = {}
|
6
|
+
OptionParser.new do |opts|
|
7
|
+
opts.on("--pattern [REGEXP]", Regexp,
|
8
|
+
"Regular expression to specify which files trigger a partial reload. " +
|
9
|
+
"By default:\n #{Mr::Sparkle::DEFAULT_RELOAD_PATTERN.to_s}") do |rx|
|
10
|
+
options[:pattern] = rx
|
11
|
+
end
|
12
|
+
opts.on("--full-reload-pattern [REGEXP]", Regexp,
|
13
|
+
"Regular expression to specify which files trigger a full reload. " +
|
14
|
+
"By default:\n #{Mr::Sparkle::DEFAULT_FULL_RELOAD_PATTERN.to_s}") do |rx|
|
15
|
+
options[:full] = rx
|
16
|
+
end
|
17
|
+
end.parse!
|
18
|
+
|
19
|
+
Mr::Sparkle::Daemon.new(options, ARGV).run
|
data/lib/mr-sparkle.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require_relative "mr-sparkle/version"
|
2
|
+
require 'listen'
|
3
|
+
|
4
|
+
module Mr
|
5
|
+
module Sparkle
|
6
|
+
|
7
|
+
DEFAULT_RELOAD_PATTERN = /\.(?:builder|coffee|creole|css|erb|erubis|haml|html|js|less|liquid|mab|markdown|md|mdown|mediawiki|mkd|mw|nokogiri|radius|rb|rdoc|rhtml|ru|sass|scss|str|textile|txt|wiki|yajl|yml)$/
|
8
|
+
|
9
|
+
DEFAULT_FULL_RELOAD_PATTERN = /^Gemfile$/
|
10
|
+
|
11
|
+
class Daemon
|
12
|
+
|
13
|
+
def initialize(options, unicorn_args)
|
14
|
+
@reload_pattern = options[:pattern] || DEFAULT_RELOAD_PATTERN
|
15
|
+
@full_reload_pattern = options[:full] || DEFAULT_FULL_RELOAD_PATTERN
|
16
|
+
@unicorn_args = unicorn_args
|
17
|
+
end
|
18
|
+
|
19
|
+
def start_unicorn
|
20
|
+
Kernel.spawn('unicorn', '-c',
|
21
|
+
File.expand_path('mr-sparkle/unicorn.conf.rb',File.dirname(__FILE__)),
|
22
|
+
*@unicorn_args)
|
23
|
+
end
|
24
|
+
|
25
|
+
def run()
|
26
|
+
puts "Reload pattern: #{@reload_pattern}"
|
27
|
+
@unicorn_pid = start_unicorn
|
28
|
+
listener = Listen.to('.', :relative_paths=>true)
|
29
|
+
listener.filter(@full_reload_pattern)
|
30
|
+
listener.filter(@reload_pattern)
|
31
|
+
listener.change do |modified, added, removed|
|
32
|
+
puts "File change event detected: #{[modified, added, removed].inspect}"
|
33
|
+
if (modified + added + removed).index {|f| f =~ @full_reload_pattern}
|
34
|
+
# Reload everything. Perhaps this could use the "procedure to
|
35
|
+
# replace a running unicorn executable" described at:
|
36
|
+
# http://unicorn.bogomips.org/SIGNALS.html
|
37
|
+
# but one wouldn't expect this to be triggered all that much,
|
38
|
+
# and this is just way simpler for now.
|
39
|
+
Process.kill(:QUIT, @unicorn_pid)
|
40
|
+
Process.wait(@unicorn_pid)
|
41
|
+
@unicorn_pid = start_unicorn.call
|
42
|
+
else
|
43
|
+
# Send a HUP to unicorn to tell it to gracefully shut down its
|
44
|
+
# workers
|
45
|
+
Process.kill(:HUP, @unicorn_pid)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
shutdown = lambda do |signal|
|
50
|
+
listener.stop
|
51
|
+
Process.kill(:TERM, @unicorn_pid)
|
52
|
+
Process.wait(@unicorn_pid)
|
53
|
+
exit
|
54
|
+
end
|
55
|
+
Signal.trap(:INT, &shutdown)
|
56
|
+
Signal.trap(:EXIT, &shutdown)
|
57
|
+
|
58
|
+
# Ideally we would start the listener in a blocking mode and have it
|
59
|
+
# just work. But unfortunately listener.stop will not work from a
|
60
|
+
# signal on the same thread the listener is running.
|
61
|
+
# So we need to start it in the background, then keep this thread
|
62
|
+
# alive just so it can wait to be interrupted.
|
63
|
+
listener.start(false)
|
64
|
+
sleep(99999) while true
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
data/mr-sparkle.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'mr-sparkle/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "mr-sparkle"
|
8
|
+
gem.version = Mr::Sparkle::VERSION
|
9
|
+
gem.authors = ["Micah Chalmer"]
|
10
|
+
gem.email = ["micah@micahchalmer.net"]
|
11
|
+
gem.description = %q{This gem contains a script to start a Unicorn-based server for your Rack application that reloads your automatically when they are changed, but doesn't incur the penalty of reloading all the gem dependencies. It's based on Jonathan D. Stott's blog post "Magical Reloading Sparkles"--hence the name.}
|
12
|
+
gem.summary = %q{Runs Unicorn, automatically reloading the application, but not bundled gems.}
|
13
|
+
gem.homepage = "http://github.com/MicahChalmer/mr-sparkle"
|
14
|
+
gem.required_ruby_version = '>= 1.9.1'
|
15
|
+
|
16
|
+
gem.files = `git ls-files`.split($/)
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
|
+
gem.require_paths = ["lib"]
|
20
|
+
gem.add_dependency("unicorn", '~>4.5.0')
|
21
|
+
gem.add_dependency("listen", '~>0.6.0')
|
22
|
+
|
23
|
+
# Only one of these can actually be used on a given platform, but they
|
24
|
+
# can both be installed OK--see the note about this at:
|
25
|
+
# https://github.com/guard/guard#efficient-filesystem-handling
|
26
|
+
gem.add_dependency('rb-inotify', '~>0.8.8')
|
27
|
+
gem.add_dependency('rb-fsevent', '~>0.9.2')
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mr-sparkle
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Micah Chalmer
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-12-23 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: unicorn
|
16
|
+
requirement: &77182970 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 4.5.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *77182970
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: listen
|
27
|
+
requirement: &77182700 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.6.0
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *77182700
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rb-inotify
|
38
|
+
requirement: &77182400 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 0.8.8
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *77182400
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rb-fsevent
|
49
|
+
requirement: &77182140 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.9.2
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *77182140
|
58
|
+
description: This gem contains a script to start a Unicorn-based server for your Rack
|
59
|
+
application that reloads your automatically when they are changed, but doesn't incur
|
60
|
+
the penalty of reloading all the gem dependencies. It's based on Jonathan D. Stott's
|
61
|
+
blog post "Magical Reloading Sparkles"--hence the name.
|
62
|
+
email:
|
63
|
+
- micah@micahchalmer.net
|
64
|
+
executables:
|
65
|
+
- mr-sparkle
|
66
|
+
extensions: []
|
67
|
+
extra_rdoc_files: []
|
68
|
+
files:
|
69
|
+
- .gitignore
|
70
|
+
- Gemfile
|
71
|
+
- LICENSE.txt
|
72
|
+
- README.md
|
73
|
+
- Rakefile
|
74
|
+
- bin/mr-sparkle
|
75
|
+
- lib/mr-sparkle.rb
|
76
|
+
- lib/mr-sparkle/unicorn.conf.rb
|
77
|
+
- lib/mr-sparkle/version.rb
|
78
|
+
- mr-sparkle.gemspec
|
79
|
+
homepage: http://github.com/MicahChalmer/mr-sparkle
|
80
|
+
licenses: []
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ! '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 1.9.1
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
none: false
|
93
|
+
requirements:
|
94
|
+
- - ! '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
requirements: []
|
98
|
+
rubyforge_project:
|
99
|
+
rubygems_version: 1.8.17
|
100
|
+
signing_key:
|
101
|
+
specification_version: 3
|
102
|
+
summary: Runs Unicorn, automatically reloading the application, but not bundled gems.
|
103
|
+
test_files: []
|