halcyon 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +1 -0
- data/LICENSE +20 -0
- data/README +107 -0
- data/Rakefile +8 -6
- data/bin/halcyon +3 -204
- data/lib/halcyon.rb +55 -42
- data/lib/halcyon/application.rb +247 -0
- data/lib/halcyon/application/router.rb +86 -0
- data/lib/halcyon/client.rb +187 -35
- data/lib/halcyon/client/ssl.rb +38 -0
- data/lib/halcyon/controller.rb +154 -0
- data/lib/halcyon/exceptions.rb +67 -59
- data/lib/halcyon/logging.rb +31 -0
- data/lib/halcyon/logging/analogger.rb +31 -0
- data/lib/halcyon/logging/helpers.rb +37 -0
- data/lib/halcyon/logging/log4r.rb +25 -0
- data/lib/halcyon/logging/logger.rb +20 -0
- data/lib/halcyon/logging/logging.rb +19 -0
- data/lib/halcyon/runner.rb +141 -0
- data/lib/halcyon/runner/commands.rb +141 -0
- data/lib/halcyon/runner/helpers.rb +9 -0
- data/lib/halcyon/runner/helpers/command_helper.rb +71 -0
- data/spec/halcyon/application_spec.rb +70 -0
- data/spec/halcyon/client_spec.rb +63 -0
- data/spec/halcyon/controller_spec.rb +68 -0
- data/spec/halcyon/halcyon_spec.rb +63 -0
- data/spec/halcyon/logging_spec.rb +31 -0
- data/spec/halcyon/router_spec.rb +37 -12
- data/spec/halcyon/runner_spec.rb +54 -0
- data/spec/spec_helper.rb +75 -9
- data/support/generators/halcyon/USAGE +0 -0
- data/support/generators/halcyon/halcyon_generator.rb +52 -0
- data/support/generators/halcyon/templates/README +26 -0
- data/support/generators/halcyon/templates/Rakefile +32 -0
- data/support/generators/halcyon/templates/app/application.rb +43 -0
- data/support/generators/halcyon/templates/config/config.yml +36 -0
- data/support/generators/halcyon/templates/config/init/environment.rb +11 -0
- data/support/generators/halcyon/templates/config/init/hooks.rb +39 -0
- data/support/generators/halcyon/templates/config/init/requires.rb +10 -0
- data/support/generators/halcyon/templates/config/init/routes.rb +50 -0
- data/support/generators/halcyon/templates/lib/client.rb +77 -0
- data/support/generators/halcyon/templates/runner.ru +8 -0
- data/support/generators/halcyon_flat/USAGE +0 -0
- data/support/generators/halcyon_flat/halcyon_flat_generator.rb +52 -0
- data/support/generators/halcyon_flat/templates/README +26 -0
- data/support/generators/halcyon_flat/templates/Rakefile +32 -0
- data/support/generators/halcyon_flat/templates/app.rb +49 -0
- data/support/generators/halcyon_flat/templates/lib/client.rb +17 -0
- data/support/generators/halcyon_flat/templates/runner.ru +8 -0
- metadata +73 -20
- data/lib/halcyon/client/base.rb +0 -261
- data/lib/halcyon/client/exceptions.rb +0 -41
- data/lib/halcyon/client/router.rb +0 -106
- data/lib/halcyon/server.rb +0 -62
- data/lib/halcyon/server/auth/basic.rb +0 -107
- data/lib/halcyon/server/base.rb +0 -774
- data/lib/halcyon/server/exceptions.rb +0 -41
- data/lib/halcyon/server/router.rb +0 -103
- data/spec/halcyon/error_spec.rb +0 -55
- data/spec/halcyon/server_spec.rb +0 -105
File without changes
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
|
3
|
+
class HalcyonGenerator < RubiGen::Base
|
4
|
+
DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'],
|
5
|
+
Config::CONFIG['ruby_install_name'])
|
6
|
+
|
7
|
+
default_options :shebang => DEFAULT_SHEBANG
|
8
|
+
|
9
|
+
attr_reader :app_name, :module_name
|
10
|
+
|
11
|
+
def initialize(runtime_args, runtime_options = {})
|
12
|
+
super
|
13
|
+
usage if args.empty?
|
14
|
+
@destination_root = args.shift
|
15
|
+
@app_name = File.basename(File.expand_path(@destination_root))
|
16
|
+
@module_name = app_name.camelize
|
17
|
+
# extract_options
|
18
|
+
end
|
19
|
+
|
20
|
+
def manifest
|
21
|
+
record do |m|
|
22
|
+
source = File.expand_path(File.join(File.dirname(__FILE__), 'templates')) << File::Separator
|
23
|
+
|
24
|
+
m.directory ''
|
25
|
+
Dir[source + '**/*'].each do |path|
|
26
|
+
path = path.sub(source, '')
|
27
|
+
if File.directory?(source+path)
|
28
|
+
m.directory path
|
29
|
+
else
|
30
|
+
m.template path, path, :assigns => {:app => @module_name, :unix => @app_name}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
m.directory 'log'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
def banner
|
39
|
+
<<-"end;"
|
40
|
+
Create a stub for #{File.basename $0} to get started.
|
41
|
+
|
42
|
+
Usage: #{File.basename $0} [options] /path/to/your/app"
|
43
|
+
end;
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_options!(opts)
|
47
|
+
opts.separator ''
|
48
|
+
opts.separator "#{File.basename $0} options:"
|
49
|
+
opts.on("-v", "--version", "Show the #{File.basename($0)} version number and quit.")
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
%w(rubygems rake rake/clean rake/rdoctask fileutils pp halcyon).each{|dep|require dep}
|
2
|
+
|
3
|
+
include FileUtils
|
4
|
+
|
5
|
+
# Halcyon.root => the root application directory
|
6
|
+
# Halcyon.app => the application name
|
7
|
+
|
8
|
+
desc "Start the application on port 4647"
|
9
|
+
task :start do
|
10
|
+
sh "halcyon start -p 4647"
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "Generate RDoc documentation"
|
14
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
15
|
+
rdoc.options << '--line-numbers' << '--inline-source' <<
|
16
|
+
'--main' << 'README' <<
|
17
|
+
'--title' << "#{Halcyon.app} Documentation" <<
|
18
|
+
'--charset' << 'utf-8'
|
19
|
+
rdoc.rdoc_dir = "doc"
|
20
|
+
rdoc.rdoc_files.include('README')
|
21
|
+
rdoc.rdoc_files.include('app/**/*.rb')
|
22
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
23
|
+
end
|
24
|
+
|
25
|
+
# = Custom Rake Tasks
|
26
|
+
#
|
27
|
+
# Add your custom rake tasks here.
|
28
|
+
|
29
|
+
# ...
|
30
|
+
|
31
|
+
# = Default Task
|
32
|
+
task :default => Rake::Task['start']
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class Application < Halcyon::Controller
|
2
|
+
|
3
|
+
# Summary of available services
|
4
|
+
#
|
5
|
+
# This method is just a sample and can be removed altogether. It may be
|
6
|
+
# advised to advertise available functionality for public-facing applications
|
7
|
+
# to clarify usage.
|
8
|
+
#
|
9
|
+
# Returns [{:path=>String:path,Symbol:method=>[Hash:params, *:sample],...},...]
|
10
|
+
def index
|
11
|
+
ok([
|
12
|
+
{
|
13
|
+
:path => "/time",
|
14
|
+
:GET => [{}, Time.now.to_s],
|
15
|
+
:POST => [{:format => "strftime compatible"}, Time.now.strftime("%Y-%m-%dT%H:%M%:%S%Z")]
|
16
|
+
},
|
17
|
+
{
|
18
|
+
:path => "/",
|
19
|
+
:GET => [{}, "[{:path=>String:path,Symbol:method=>[Hash:params, *:sample],...},...]"]
|
20
|
+
}
|
21
|
+
])
|
22
|
+
end
|
23
|
+
|
24
|
+
# Responds with the current time in either generic or specific formats
|
25
|
+
#
|
26
|
+
# GET
|
27
|
+
# returns the current time
|
28
|
+
# POST
|
29
|
+
# returns the current time formatted acccording to +format+
|
30
|
+
#
|
31
|
+
# Returns String:time
|
32
|
+
def time
|
33
|
+
case method
|
34
|
+
when :get
|
35
|
+
ok(Time.now.to_s)
|
36
|
+
when :post
|
37
|
+
ok(Time.now.strftime(post[:format] || "%Y-%m-%dT%H:%M%:%S%Z"))
|
38
|
+
else
|
39
|
+
raise NotImplemented.new
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# = Framework
|
2
|
+
#
|
3
|
+
# <tt>allow_from</tt>: specifies what connections to accept;
|
4
|
+
# * <tt>all</tt>: allow connections from all clients
|
5
|
+
# * <tt>local</tt>: only allow connections from the same host (localhost et al)
|
6
|
+
# * <tt>halcyon_clients</tt>: only Halcyon clients (tests the User-Agent only)
|
7
|
+
allow_from: all
|
8
|
+
|
9
|
+
# = Environment
|
10
|
+
#
|
11
|
+
# Uncomment to manually specify the environment to run the application in.
|
12
|
+
# Defaults to <tt>:development</tt>.
|
13
|
+
#
|
14
|
+
# environment: production
|
15
|
+
|
16
|
+
# = Logging
|
17
|
+
#
|
18
|
+
# Configures the logging client in the framework, including destination,
|
19
|
+
# level filter, and what logger to use.
|
20
|
+
#
|
21
|
+
# <tt>type</tt>: the logger to use (defaults to Ruby's <tt>Logger</tt>)
|
22
|
+
# * <tt>Logger</tt>
|
23
|
+
# * <tt>Analogger</tt>
|
24
|
+
# * <tt>Logging</tt>
|
25
|
+
# * <tt>Log4r</tt>
|
26
|
+
# <tt>file</tt>: the log file; leave unset for STDOUT
|
27
|
+
# <tt>level</tt>: the message filter level (default to <tt>debug</tt>)
|
28
|
+
# * specific to the client used, often is: debug, info, warn, error, fatal
|
29
|
+
logging:
|
30
|
+
type: Logger
|
31
|
+
# file: # STDOUT
|
32
|
+
level: debug
|
33
|
+
|
34
|
+
# = Application
|
35
|
+
#
|
36
|
+
# Your application-specific configuration options here.
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# = Environment
|
2
|
+
#
|
3
|
+
# Sets the environment unless already set.
|
4
|
+
#
|
5
|
+
# Creates the <tt>Halcyon.environment</tt> configurable attribute. Since this
|
6
|
+
# is mapped to <tt>Halcyon.config[:environment]</tt>, environment can be set
|
7
|
+
# by setting the <tt>environment:</tt> configuration value in the
|
8
|
+
# <tt>config/config.yml</tt> file.
|
9
|
+
|
10
|
+
Halcyon.configurable_attr(:environment)
|
11
|
+
Halcyon.environment = :development unless Halcyon.environment
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# = Hooks
|
2
|
+
#
|
3
|
+
# Specify actions to run at specific times during the application's execution,
|
4
|
+
# such as the startup or shutdown events.
|
5
|
+
#
|
6
|
+
# Available Hooks
|
7
|
+
# +startup+
|
8
|
+
# +shutdown+
|
9
|
+
#
|
10
|
+
# All hooks take the current application configuration as its sole block param.
|
11
|
+
#
|
12
|
+
# Examples
|
13
|
+
# Halcyon::Application.startup do |config|
|
14
|
+
# # Halcyon.db set in config/initialize/database.rb
|
15
|
+
# ::DB = Sequel(Halcyon.db)
|
16
|
+
# logger.info "Connected to database"
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# The +logger+ object is available to log messages on status or otherwise.
|
20
|
+
|
21
|
+
# = Startup
|
22
|
+
#
|
23
|
+
# Run when the Halcyon::Application object is instanciated, after all
|
24
|
+
# initializers are loaded.
|
25
|
+
#
|
26
|
+
# Ideal for establishing connections to resources like databases.
|
27
|
+
# Establish configuration options in initializer files, though.
|
28
|
+
Halcyon::Application.startup do |config|
|
29
|
+
logger.info 'Define startup tasks in config/init/hooks.rb'
|
30
|
+
end
|
31
|
+
|
32
|
+
# = Shutdown
|
33
|
+
#
|
34
|
+
# Run <tt>at_exit</tt>. Should run in most cases of termination.
|
35
|
+
#
|
36
|
+
# Ideal for closing connections to resources.
|
37
|
+
Halcyon::Application.shutdown do |config|
|
38
|
+
logger.info 'Define shutdown tasks in config/init/hooks.rb'
|
39
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# = Required Libraries
|
2
|
+
#
|
3
|
+
# Specify required libraries specific to the operation of your application.
|
4
|
+
#
|
5
|
+
# Examples
|
6
|
+
# %(digest/md5 tempfile date).each{|dep|require dep}
|
7
|
+
#
|
8
|
+
# RubyGems should already be required by Halcyon, so don't include it.
|
9
|
+
|
10
|
+
%w().each{|dep|require dep}
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# = Routes
|
2
|
+
#
|
3
|
+
# Halcyon::Application::Router is the request routing mapper for the merb
|
4
|
+
# framework.
|
5
|
+
#
|
6
|
+
# You can route a specific URL to a controller / action pair:
|
7
|
+
#
|
8
|
+
# r.match("/contact").
|
9
|
+
# to(:controller => "info", :action => "contact")
|
10
|
+
#
|
11
|
+
# You can define placeholder parts of the url with the :symbol notation. These
|
12
|
+
# placeholders will be available in the params hash of your controllers. For example:
|
13
|
+
#
|
14
|
+
# r.match("/books/:book_id/:action").
|
15
|
+
# to(:controller => "books")
|
16
|
+
#
|
17
|
+
# Or, use placeholders in the "to" results for more complicated routing, e.g.:
|
18
|
+
#
|
19
|
+
# r.match("/admin/:module/:controller/:action/:id").
|
20
|
+
# to(:controller => ":module/:controller")
|
21
|
+
#
|
22
|
+
# You can also use regular expressions, deferred routes, and many other options.
|
23
|
+
# See merb/specs/merb/router.rb for a fairly complete usage sample.
|
24
|
+
#
|
25
|
+
# Stolen directly from generated Merb app. All documentation applies.
|
26
|
+
# Read more about the Merb router at http://merbivore.com/.
|
27
|
+
Halcyon::Application.route do |r|
|
28
|
+
|
29
|
+
# Sample route for the sample functionality in Application.
|
30
|
+
# Safe to remove!
|
31
|
+
r.match('/time').to(:controller => 'application', :action => 'time')
|
32
|
+
|
33
|
+
# RESTful routes
|
34
|
+
# r.resources :posts
|
35
|
+
|
36
|
+
# This is the default route for /:controller/:action/:id
|
37
|
+
# This is fine for most cases. If you're heavily using resource-based
|
38
|
+
# routes, you may want to comment/remove this line to prevent
|
39
|
+
# clients from calling your create or destroy actions with a GET
|
40
|
+
r.default_routes
|
41
|
+
|
42
|
+
# Change this for the default route to be available at /
|
43
|
+
r.match('/').to(:controller => 'application', :action => 'index')
|
44
|
+
# It can often be useful to respond with available functionality if the
|
45
|
+
# application is a public-facing service.
|
46
|
+
|
47
|
+
# Default not-found route
|
48
|
+
{:action => 'not_found'}
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
%w(rubygems halcyon).each{|dep|require dep}
|
2
|
+
|
3
|
+
module <%= app %>
|
4
|
+
|
5
|
+
# = Client
|
6
|
+
#
|
7
|
+
# The client interface for accessing services provided by the Halcyon
|
8
|
+
# application as defined in the controllers in <tt>app/</tt>.
|
9
|
+
#
|
10
|
+
# == Usage
|
11
|
+
#
|
12
|
+
# To use the Client in your application, create an instance and call methods
|
13
|
+
# defined here. For example:
|
14
|
+
#
|
15
|
+
# client = <%= app %>::Client.new('http://localhost:4647/')
|
16
|
+
# client.time #=> "Tue Apr 15 21:04:15 -0400 2008"
|
17
|
+
#
|
18
|
+
# You can just as easily call the primary <tt>get</tt>, <tt>post</tt>,
|
19
|
+
# <tt>put</tt>, and <tt>delete</tt> methods as well, passing in the +path+
|
20
|
+
# and any params. For example:
|
21
|
+
#
|
22
|
+
# client.get('/time') #=> "Tue Apr 15 21:04:15 -0400 2008"
|
23
|
+
#
|
24
|
+
# By default, if you enter a bad (non-existent) path or the application
|
25
|
+
# raises an exception and cannot complete successfully, the standard response
|
26
|
+
# format will be returned but with more appropriate +status+ and +body+
|
27
|
+
# values. For instance:
|
28
|
+
#
|
29
|
+
# client.get('/nonexistent/path') #=> {:status=>404,:body=>"Not Found"}
|
30
|
+
#
|
31
|
+
# Exceptions can be raised on any +status+ returned other than +200+ if you
|
32
|
+
# set <tt>Halcyon::Client#raise_exceptions!</tt> to +true+ (which is the
|
33
|
+
# default param).
|
34
|
+
#
|
35
|
+
# client.raise_exceptions! #=> true
|
36
|
+
# client.get('/nonexistent/path') #=> NotFound exception is raised
|
37
|
+
#
|
38
|
+
# These exceptions all inherit from <tt>Halcyon::Exceptions::Base</tt> so
|
39
|
+
# <tt>rescue</tt>ing just normal Halcyon errors is trivial.
|
40
|
+
#
|
41
|
+
# However, setting this value can cause the meaning and the appropriate
|
42
|
+
# error-handling measures put in place in actions. Although each method
|
43
|
+
# could just as easily set the +raise_exceptions+ configuration option
|
44
|
+
# itself, it is not advised to do so due to the possibility of non-
|
45
|
+
# consistent and confusing behavior it can cause.
|
46
|
+
#
|
47
|
+
# If raising exceptions is preferred, it should be set as soon as the
|
48
|
+
# client is created and the client methods should be designed accordingly.
|
49
|
+
class Client < Halcyon::Client
|
50
|
+
|
51
|
+
def self.version
|
52
|
+
VERSION.join('.')
|
53
|
+
end
|
54
|
+
|
55
|
+
# Sample of a client method to wrap an API call
|
56
|
+
def time
|
57
|
+
get('/time')[:body]
|
58
|
+
end
|
59
|
+
|
60
|
+
# More comprehensive sample client method
|
61
|
+
def strftime(format = nil)
|
62
|
+
if (res = post('/time', :format => format))[:status] == 200
|
63
|
+
# success
|
64
|
+
res[:body]
|
65
|
+
else
|
66
|
+
# failure
|
67
|
+
# returns the whole response to indicate an error happened, but could
|
68
|
+
# just as easily raise an exception.
|
69
|
+
# but if an exception is prefererd, read the documentation for
|
70
|
+
# <tt>Halcyon::Client#raise_exceptions!</tt> instead.
|
71
|
+
res
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
File without changes
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
|
3
|
+
class HalcyonFlatGenerator < RubiGen::Base
|
4
|
+
DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'],
|
5
|
+
Config::CONFIG['ruby_install_name'])
|
6
|
+
|
7
|
+
default_options :shebang => DEFAULT_SHEBANG
|
8
|
+
|
9
|
+
attr_reader :app_name, :module_name
|
10
|
+
|
11
|
+
def initialize(runtime_args, runtime_options = {})
|
12
|
+
super
|
13
|
+
usage if args.empty?
|
14
|
+
@destination_root = args.shift
|
15
|
+
@app_name = File.basename(File.expand_path(@destination_root))
|
16
|
+
@module_name = app_name.camelize
|
17
|
+
# extract_options
|
18
|
+
end
|
19
|
+
|
20
|
+
def manifest
|
21
|
+
record do |m|
|
22
|
+
source = File.expand_path(File.join(File.dirname(__FILE__), 'templates')) << File::Separator
|
23
|
+
|
24
|
+
m.directory ''
|
25
|
+
Dir[source + '**/*'].each do |path|
|
26
|
+
path = path.sub(source, '')
|
27
|
+
if File.directory?(source+path)
|
28
|
+
m.directory path
|
29
|
+
else
|
30
|
+
m.template path, path, :assigns => {:app => @module_name, :unix => @app_name}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
m.directory 'log'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
def banner
|
39
|
+
<<-"end;"
|
40
|
+
Create a stub for #{File.basename $0} to get started.
|
41
|
+
|
42
|
+
Usage: #{File.basename $0} [options] /path/to/your/app"
|
43
|
+
end;
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_options!(opts)
|
47
|
+
opts.separator ''
|
48
|
+
opts.separator "#{File.basename $0} options:"
|
49
|
+
opts.on("-v", "--version", "Show the #{File.basename($0)} version number and quit.")
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|