clearwater-hot_loader 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/README.md +85 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/clearwater-hot_loader.gemspec +27 -0
- data/lib/clearwater/hot_loader/configuration.rb +29 -0
- data/lib/clearwater/hot_loader/file_listener.rb +21 -0
- data/lib/clearwater/hot_loader/server.rb +46 -0
- data/lib/clearwater/hot_loader/version.rb +5 -0
- data/lib/clearwater/hot_loader.rb +64 -0
- data/opal/clearwater/hot_loader.rb +35 -0
- metadata +143 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 62c2fa540fc31b83554e87857a0fa030fb28fd5f
|
4
|
+
data.tar.gz: 48acf0cb240586d81b00f681810731c149eaf75a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5eb6383c99d0897ba6f5dc9cd910c5657866d7bae1c9672b1e2d7b6091802b22eda30004d3fd22411e0da6b2c332c63bbf5c8f093fe311682c7946c5f25c088d
|
7
|
+
data.tar.gz: 549ef88113da09433a95cdc7b6a0cd0d2402550994dde6ab4d70ee39c14b3c18e26b6c4b139bbee7764755a6c93514ffe3134e751de2ea292ce85c612ae8cfaa
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
4
|
+
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
|
6
|
+
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
8
|
+
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
10
|
+
|
11
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
12
|
+
|
13
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# Clearwater::HotLoader
|
2
|
+
|
3
|
+
A complete solution for re-rendering a Clearwater app in development without a full-page reload.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
In your `Gemfile`:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
# This gem is only useful in the development environment
|
11
|
+
gem 'clearwater-hot_loader', group: :development
|
12
|
+
```
|
13
|
+
|
14
|
+
Then run `bundle` to get it installed.
|
15
|
+
|
16
|
+
## Usage
|
17
|
+
|
18
|
+
In order to get hot-loading working, we need to connect three parts:
|
19
|
+
|
20
|
+
1. The filesystem monitor to trigger changes
|
21
|
+
2. WebSocket server to notify the client about changes
|
22
|
+
3. WebSocket client to receive the push from the server
|
23
|
+
|
24
|
+
The examples in this README will have examples for Rails, but we only rely on Rack.
|
25
|
+
|
26
|
+
### Filesystem Monitor
|
27
|
+
|
28
|
+
You kick off the filesystem monitor by running `Clearwater::HotLoader.start`.
|
29
|
+
|
30
|
+
For a Rails app, simply add that command to the end of your `config/environments/development.rb` file to ensure it only gets run in development.
|
31
|
+
|
32
|
+
For Rack apps that aren't based on Rails, you can add this to your `config.ru` file (above the line beginning with `run`):
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
if ENV['RACK_ENV'] == 'development'
|
36
|
+
Clearwater::HotLoader.start
|
37
|
+
end
|
38
|
+
```
|
39
|
+
|
40
|
+
### WebSocket Server
|
41
|
+
|
42
|
+
The client needs to know what to connect to in order to listen for changes, so we need to add an endpoint to our web app to make this happen. How you do this depends entirely on which back-end framework you're using, but most Rack-based frameworks (including Rails) have a way to mount another Rack app as the handler for an endpoint.
|
43
|
+
|
44
|
+
_Note: You want to ensure you're only mounting it in development._
|
45
|
+
|
46
|
+
In Rails, add this to your `config/routes.rb` file:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
mount Clearwater::HotLoader, at: '/clearwater_hot_loader' if Rails.env.development?
|
50
|
+
```
|
51
|
+
|
52
|
+
The `Clearwater::HotLoader` module contains a Rack server which will handle a WebSocket connection. The filesystem monitor notifies the WebSocket server when files have changed.
|
53
|
+
|
54
|
+
### WebSocket Client
|
55
|
+
|
56
|
+
The WebSocket client listens for updates from the server and executes them, updating the Ruby environment running inside the browser, and then re-renders your Clearwater app. If you have multiple Clearwater apps running on the page, it will re-render all of them.
|
57
|
+
|
58
|
+
To connect to the server, simply add this to the top of your Clearwater app:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
require 'clearwater/hot_loader'
|
62
|
+
Clearwater::HotLoader.connect port
|
63
|
+
```
|
64
|
+
|
65
|
+
Simply replace `port` with the port number on which your app is running. It will be the same port on which your web app is running. For example, if you usually open `localhost:3000` to load your web app in the browser, you'll want to connect to port 3000.
|
66
|
+
|
67
|
+
## Caveats
|
68
|
+
|
69
|
+
- Hot loading involves patching classes and objects that are already loaded. If your code is executing as if being run for the first time, existing app state may be clobbered with fresh state.
|
70
|
+
- `require` statements are ignored for various reasons:
|
71
|
+
- Reloading all dependencies is nearly always unnecessary
|
72
|
+
- The Opal environment is not designed to be idempotent. Reloading it may clobber the one already loaded.
|
73
|
+
- Because of that, if you add an outside dependency, you will likely need to refresh the page.
|
74
|
+
|
75
|
+
## Development
|
76
|
+
|
77
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
78
|
+
|
79
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
80
|
+
|
81
|
+
## Contributing
|
82
|
+
|
83
|
+
Bug reports and pull requests are welcome on [GitHub](https://github.com/clearwater-rb/clearwater-hot_loader).
|
84
|
+
|
85
|
+
This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "clearwater/hot_loader"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'clearwater/hot_loader/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "clearwater-hot_loader"
|
8
|
+
spec.version = Clearwater::HotLoader::VERSION
|
9
|
+
spec.authors = ["Jamie Gaskins"]
|
10
|
+
spec.email = ["jgaskins@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Reload your Clearwater app without refreshing the browser}
|
13
|
+
spec.homepage = "https://github.com/clearwater-rb/clearwater-hot_loader"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
16
|
+
spec.bindir = "exe"
|
17
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_runtime_dependency 'clearwater'
|
21
|
+
spec.add_runtime_dependency 'listen'
|
22
|
+
spec.add_runtime_dependency 'faye-websocket'
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency "rspec"
|
27
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Clearwater
|
2
|
+
module HotLoader
|
3
|
+
class Configuration
|
4
|
+
attr_reader :directories
|
5
|
+
|
6
|
+
def initialize attributes={}
|
7
|
+
candidate_directories = Dir[
|
8
|
+
*%w(
|
9
|
+
app/assets/javascripts
|
10
|
+
app/assets/javascripts/**/*
|
11
|
+
assets
|
12
|
+
assets/**/*
|
13
|
+
)
|
14
|
+
]
|
15
|
+
self.directories = candidate_directories
|
16
|
+
|
17
|
+
attributes.each do |attr, value|
|
18
|
+
public_send "#{attr}=", value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def directories= directories
|
23
|
+
@directories = Array(directories).select { |f|
|
24
|
+
File.directory?(f)
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'listen'
|
2
|
+
|
3
|
+
module Clearwater
|
4
|
+
module HotLoader
|
5
|
+
class FileListener
|
6
|
+
def initialize directory, &block
|
7
|
+
@directory = directory
|
8
|
+
|
9
|
+
@listener = Listen.to(directory) do |modified, added, removed|
|
10
|
+
(modified + added).each do |file|
|
11
|
+
block.call(file)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def start
|
17
|
+
@listener.start
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'opal'
|
2
|
+
require 'faye/websocket'
|
3
|
+
|
4
|
+
module Clearwater
|
5
|
+
module HotLoader
|
6
|
+
class Server
|
7
|
+
include Faye
|
8
|
+
|
9
|
+
attr_reader :hot_loader
|
10
|
+
|
11
|
+
def initialize hot_loader=HotLoader
|
12
|
+
@hot_loader = hot_loader
|
13
|
+
end
|
14
|
+
|
15
|
+
def call env
|
16
|
+
if WebSocket.websocket? env
|
17
|
+
ws = WebSocket.new(env)
|
18
|
+
|
19
|
+
ws.on :close do
|
20
|
+
hot_loader.remove_socket ws
|
21
|
+
end
|
22
|
+
|
23
|
+
hot_loader.add_socket ws
|
24
|
+
|
25
|
+
ws.rack_response
|
26
|
+
else
|
27
|
+
[200, { 'Content-Type' => 'text/plain' }, ['Websockets only, please.']]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def compile_file filename
|
32
|
+
code = File.read(filename)
|
33
|
+
|
34
|
+
if filename.end_with? '.rb'
|
35
|
+
Opal.compile(code)
|
36
|
+
elsif filename.end_with? '.js'
|
37
|
+
code
|
38
|
+
else
|
39
|
+
''
|
40
|
+
end
|
41
|
+
rescue => e
|
42
|
+
''
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'opal'
|
2
|
+
require 'clearwater/hot_loader/server'
|
3
|
+
require 'clearwater/hot_loader/file_listener'
|
4
|
+
require 'clearwater/hot_loader/configuration'
|
5
|
+
require 'set'
|
6
|
+
|
7
|
+
module Clearwater
|
8
|
+
module HotLoader
|
9
|
+
module_function
|
10
|
+
|
11
|
+
def configure
|
12
|
+
yield configuration
|
13
|
+
end
|
14
|
+
|
15
|
+
def configuration
|
16
|
+
@configuration ||= Configuration.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def call env
|
20
|
+
server.call env
|
21
|
+
end
|
22
|
+
|
23
|
+
def server
|
24
|
+
@server ||= Server.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def start
|
28
|
+
@listeners = configuration.directories.map { |dir|
|
29
|
+
puts "[Clearwater::HotLoader] Monitoring #{dir}"
|
30
|
+
FileListener.new(dir) do |filename|
|
31
|
+
begin
|
32
|
+
puts "[Clearwater::HotLoader] Compiling #{filename}..."
|
33
|
+
code = server.compile_file(filename)
|
34
|
+
puts "[Clearwater::HotLoader] Hot-loading #{filename}..."
|
35
|
+
|
36
|
+
sockets.each do |ws|
|
37
|
+
ws.send code
|
38
|
+
end
|
39
|
+
rescue => e
|
40
|
+
puts "ONOES! #{e.class} - #{e.message}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
}
|
44
|
+
@listeners.each(&:start)
|
45
|
+
puts "[Clearwater::HotLoader] started."
|
46
|
+
end
|
47
|
+
|
48
|
+
def sockets
|
49
|
+
@sockets ||= Set.new
|
50
|
+
end
|
51
|
+
|
52
|
+
def add_socket socket
|
53
|
+
sockets << socket
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
def remove_socket socket
|
58
|
+
sockets.delete socket
|
59
|
+
self
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
Opal.append_path File.expand_path('../../../opal', __FILE__)
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'bowser'
|
2
|
+
require 'bowser/websocket'
|
3
|
+
require 'clearwater'
|
4
|
+
|
5
|
+
module Clearwater
|
6
|
+
class HotLoader
|
7
|
+
attr_reader :port, :path
|
8
|
+
|
9
|
+
def self.connect port=3000, path='/clearwater_hot_loader'
|
10
|
+
new(port, path).connect
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize port, path='/clearwater_hot_loader'
|
14
|
+
@port = port
|
15
|
+
@path = path
|
16
|
+
end
|
17
|
+
|
18
|
+
def connect
|
19
|
+
@socket = Bowser::WebSocket.new("ws://localhost:#{port}#{path}")
|
20
|
+
|
21
|
+
@socket.on :message do |msg|
|
22
|
+
%x{ eval(msg.native.data) }
|
23
|
+
Clearwater::Application.render
|
24
|
+
end
|
25
|
+
|
26
|
+
@socket.on :close do
|
27
|
+
Bowser.window.delay 1 do
|
28
|
+
connect
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
self
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
metadata
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: clearwater-hot_loader
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jamie Gaskins
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-05-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: clearwater
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: listen
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: faye-websocket
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.10'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.10'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '10.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '10.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description:
|
98
|
+
email:
|
99
|
+
- jgaskins@gmail.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".gitignore"
|
105
|
+
- ".rspec"
|
106
|
+
- ".travis.yml"
|
107
|
+
- CODE_OF_CONDUCT.md
|
108
|
+
- Gemfile
|
109
|
+
- README.md
|
110
|
+
- Rakefile
|
111
|
+
- bin/console
|
112
|
+
- bin/setup
|
113
|
+
- clearwater-hot_loader.gemspec
|
114
|
+
- lib/clearwater/hot_loader.rb
|
115
|
+
- lib/clearwater/hot_loader/configuration.rb
|
116
|
+
- lib/clearwater/hot_loader/file_listener.rb
|
117
|
+
- lib/clearwater/hot_loader/server.rb
|
118
|
+
- lib/clearwater/hot_loader/version.rb
|
119
|
+
- opal/clearwater/hot_loader.rb
|
120
|
+
homepage: https://github.com/clearwater-rb/clearwater-hot_loader
|
121
|
+
licenses: []
|
122
|
+
metadata: {}
|
123
|
+
post_install_message:
|
124
|
+
rdoc_options: []
|
125
|
+
require_paths:
|
126
|
+
- lib
|
127
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
requirements: []
|
138
|
+
rubyforge_project:
|
139
|
+
rubygems_version: 2.5.1
|
140
|
+
signing_key:
|
141
|
+
specification_version: 4
|
142
|
+
summary: Reload your Clearwater app without refreshing the browser
|
143
|
+
test_files: []
|