Skip to content

Commit

Permalink
[Pipe] Add support client reloading (#457)
Browse files Browse the repository at this point in the history
* Add parens

Add support for client reloading

- Add Hot reloading for CSS
- Add reloading for files on public folder
- Allow reloading browser after server reload

Add development restriction

Add watcher dependency

Add reload spec

Add missing dependency

Change spec path

set host binding to 0.0.0.0 (#459)

Update database.cr

Update logger.cr

Remove duplicated dependency

Add indenting space

Using Amber Loggger

Remove watcher require

* Fixes failing spec
  • Loading branch information
faustinoaq authored and eliasjpr committed Jan 12, 2018
1 parent 11ec348 commit 3af084d
Show file tree
Hide file tree
Showing 16 changed files with 259 additions and 54 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ To test the generated app locally:
4. Start your app: `amber watch`
5. Then visit `http://0.0.0.0:3000/`

Note: `amber watch` uses [Sentry](https://github.com/samueleaton/sentry) to watch for any changes in your source files, recompiling automatically.
Note: The `amber watch` command is based on [Sentry](https://github.com/samueleaton/sentry) to watch for any changes in your source files, recompiling automatically.

If you don't want to use Sentry, you can compile and run manually:

Expand Down
7 changes: 2 additions & 5 deletions shard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ dependencies:

slang:
github: jeromegn/slang
branch: master
branch: master

redis:
github: stefanwille/crystal-redis
Expand All @@ -41,10 +41,6 @@ dependencies:
github: amberframework/teeplate
version: ~> 0.5.0

sentry:
github: samueleaton/sentry
version: ~> 0.1.0

micrate:
github: juanedi/micrate
version: ~> 0.3.0
Expand Down Expand Up @@ -72,3 +68,4 @@ dependencies:
development_dependencies:
ameba:
github: veelenga/ameba
version: ~> 0.4.0
10 changes: 9 additions & 1 deletion spec/amber/router/context_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -221,12 +221,20 @@ describe HTTP::Server::Context do
ics: "text/calendar"}.each do |format, content_type|
it "gets request format for #{content_type}" do
headers = HTTP::Headers.new
headers[HTTP::Server::Context::FORMAT_HEADER] = content_type
headers[Amber::Support::MimeTypes::FORMAT_HEADER] = content_type
request = HTTP::Request.new("GET", "/", headers)
context = create_context(request)
context.format.should eq format.to_s
end
end

%w(html json txt text xml).each do |format|
it "get format #{format} from path extension" do
request = HTTP::Request.new("GET", "/index.#{format}")
context = create_context(request)
context.format.should eq format.to_s
end
end
end

describe "#client_ip" do
Expand Down
11 changes: 9 additions & 2 deletions spec/amber/router/pipe/error_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,15 @@ module Amber
error = Error.new
pipeline = Pipeline.new
request = HTTP::Request.new("GET", "/")
Amber::Server.router.draw :web { get "/", HelloController, :index }
pipeline.build :web { plug Amber::Pipe::Logger.new }

Amber::Server.router.draw :web do
get "/", HelloController, :index
end

pipeline.build :web do
plug Amber::Pipe::Logger.new
end

error.next = ->(context : HTTP::Server::Context) { raise "Oops!" }
response = create_request_and_return_io(error, request)

Expand Down
49 changes: 49 additions & 0 deletions spec/amber/router/pipe/reload_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
require "../../../../spec_helper"

class FakeEnvironment < Amber::Environment::Env
def development?
true
end
end

module Amber
module Pipe
describe Reload do
headers = HTTP::Headers.new
headers["Accept"] = "text/html"
request = HTTP::Request.new("GET", "/reload", headers)

Amber::Server.router.draw :web do
get "/reload", HelloController, :index
end

context "when environment is in development mode" do
pipeline = Pipeline.new
pipeline.build :web do
plug Amber::Pipe::Reload.new(FakeEnvironment.new)
end
pipeline.prepare_pipelines

it "contains injected code in response.body" do
response = create_request_and_return_io(pipeline, request)

response.body.should contain "Code injected by Amber Framework"
end
end

context "when environment is NOT in development mode" do
pipeline = Pipeline.new
pipeline.build :web do
plug Amber::Pipe::Reload.new
end
pipeline.prepare_pipelines

it "does not have injected reload code in response.body" do
response = create_request_and_return_io(pipeline, request)

response.body.should_not contain "Code injected by Amber Framework"
end
end
end
end
end
2 changes: 2 additions & 0 deletions spec/spec_helper.cr
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ CURRENT_DIR = Dir.current
Amber.path = "./spec/support/config"
Amber.env=(ENV["AMBER_ENV"])
Amber.settings.redis_url = ENV["REDIS_URL"] if ENV["REDIS_URL"]?
Amber::CLI.logger.level = Logger::UNKNOWN
Amber.logger.level = Logger::UNKNOWN

require "http"
require "spec"
Expand Down
47 changes: 25 additions & 22 deletions src/amber/cli/helpers/process_runner.cr
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
require "sentry"

module Sentry
class ProcessRunner
property processes = [] of Process
property process_name : String
property files = [] of String
@logger : Amber::Environment::Logger
FILE_TIMESTAMPS = {} of String => String

def initialize(
@process_name : String,
Expand All @@ -16,6 +15,8 @@ module Sentry
files = [] of String,
@logger = Amber::CLI.logger)
@files = files
@npm_process = false
@app_running = false
end

def run
Expand All @@ -26,34 +27,35 @@ module Sentry
end

# Compiles and starts the application
#
def start_app
stop_all_processes
start_all_processes
build_result = build_app_process
if build_result && build_result.success?
stop_all_processes
create_all_processes
@app_running = true
elsif !@app_running
log "Compile time errors detected. Shutting down..."
exit 1
else
log "Unknown error occurred, Shutting down..."
exit 1
end
end

private def scan_files
file_changed = false

Dir.glob(files) do |file|
timestamp = get_timestamp(file)
if FILE_TIMESTAMPS[file]? && FILE_TIMESTAMPS[file] != timestamp
FILE_TIMESTAMPS[file] = timestamp
file_changed = true
log "#{file.capitalize.colorize(:light_gray)}"
elsif FILE_TIMESTAMPS[file]?.nil?
msg_prefix = FILE_TIMESTAMPS[file]? ? "" : "Watching file: "

if FILE_TIMESTAMPS[file]? != timestamp
FILE_TIMESTAMPS[file] = timestamp
log "#{msg_prefix}./#{file.colorize(:light_gray)}"
file_changed = true
log "Watching file: #{file.capitalize.colorize(:light_gray)}"
end
end

start_app if (file_changed)
end

private def start_all_processes
log "Compiling #{project_name}..."
create_all_processes
start_app if file_changed
end

private def stop_all_processes
Expand All @@ -65,10 +67,11 @@ module Sentry
end

private def create_all_processes
build_app_process
@processes << create_watch_process
sleep 3
create_npm_process
unless @npm_process
create_npm_process
@npm_process = true
end
end

private def build_app_process
Expand All @@ -83,7 +86,7 @@ module Sentry

private def create_npm_process
node_log "Installing dependencies..."
Process.new("npm install && npm run watch", shell: true, output: Process::Redirect::Inherit, error: Process::Redirect::Inherit)
Process.new("npm install --loglevel=error && npm run watch", shell: true, output: Process::Redirect::Inherit, error: Process::Redirect::Inherit)
node_log "Watching public directory"
end

Expand Down
3 changes: 3 additions & 0 deletions src/amber/cli/templates/app/config/routes.cr
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ Amber::Server.configure do |app|
plug Amber::Pipe::Session.new
plug Amber::Pipe::Flash.new
plug Amber::Pipe::CSRF.new

# Reload clients browsers (development only)
plug Amber::Pipe::Reload.new
end

# All static content will run these transformations
Expand Down
4 changes: 2 additions & 2 deletions src/amber/environment/logger.cr
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ require "colorize"
module Amber::Environment
class Logger < ::Logger
def puts(message, progname = progname, color = :magenta)
log(self.level, message, "#{progname}\t|".colorize(color))
log(self.level, message, "#{progname} |".colorize(color))
end

{% for name in Severity.constants %}
def {{name.id.downcase}}(message, color = :light_cyan)
log(Severity::{{name.id}}, message, "#{progname}\t|".colorize(color))
log(Severity::{{name.id}}, message, "#{progname} |".colorize(color))
end
{% end %}
end
Expand Down
16 changes: 1 addition & 15 deletions src/amber/router/context.cr
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ end
# response object. Params and Session hash can be accessed from the Context.
class HTTP::Server::Context
METHODS = %i(get post put patch delete head)
FORMAT_HEADER = "Accept"
IP_ADDRESS_HEADERS = %w(REMOTE_ADDR CLIENT_IP X_FORWARDED_FOR X_FORWARDED X_CLUSTER_CLIENT_IP FORWARDED)

include Amber::Router::Files
Expand Down Expand Up @@ -70,20 +69,7 @@ class HTTP::Server::Context
{% end %}

def format
content_type = request.headers[FORMAT_HEADER]?

if content_type
content_type = content_type.split(",").first
type = if content_type.includes?(";")
content_type.split(";").first
else
content_type
end

Amber::Support::MimeTypes.format(type)
else
Amber::Support::MimeTypes.default
end
Amber::Support::MimeTypes.get_request_format(request)
end

def port
Expand Down
3 changes: 2 additions & 1 deletion src/amber/router/params_parser.cr
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module Amber::Router
METHOD = "_method"
OVERRIDE_HEADER = "X-HTTP-Method-Override"
OVERRIDE_METHODS = %w(PATCH PUT DELETE)
TYPE_EXT_REGEX = Amber::Support::MimeTypes::TYPE_EXT_REGEX

property params = Amber::Router::Params.new

Expand Down Expand Up @@ -84,7 +85,7 @@ module Amber::Router
rparams = route.params
unless rparams.empty?
key = rparams.keys.last
rparams[key] = rparams[key].sub(Controller::Base::Content::TYPE_EXT_REGEX, "")
rparams[key] = rparams[key].sub(TYPE_EXT_REGEX, "")
end
route.params
end
Expand Down
2 changes: 1 addition & 1 deletion src/amber/router/pipe/base.cr
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module Amber

# Execution of this handler.
def call(context : HTTP::Server::Context)
call_next context
call_next(context)
end
end
end
Expand Down
26 changes: 26 additions & 0 deletions src/amber/router/pipe/reload.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
require "../../support/client_reload"

module Amber
module Pipe
# Reload clients browsers using `ClientReload`.
#
# NOTE: Amber::Pipe::Reload is intended for use in a development environment.
# ```
# pipeline :web do
# plug Amber::Pipe::Reload.new
# end
# ```
class Reload < Base
def initialize(@env : Amber::Environment::Env = Amber.env)
Support::ClientReload.new
end

def call(context : HTTP::Server::Context)
if @env.development? && context.format == "html"
context.response << Support::ClientReload::INJECTED_CODE
end
call_next(context)
end
end
end
end
2 changes: 1 addition & 1 deletion src/amber/server/server.cr
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ module Amber
loop do
begin
logger.info "Server started in #{Amber.env.colorize(:yellow)}."
logger.info "Startup Time #{Time.now - time}\n\n".colorize(:white)
logger.info "Startup Time #{Time.now - time}".colorize(:white)
server.listen(settings.port_reuse)
exit
rescue e : Errno
Expand Down
Loading

0 comments on commit 3af084d

Please sign in to comment.