dyoder-waves 0.7.3 → 0.7.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. data/app/bin/waves-console +1 -0
  2. data/app/bin/waves-server +1 -0
  3. data/app/configurations/development.rb.erb +5 -6
  4. data/app/configurations/mapping.rb.erb +1 -2
  5. data/app/configurations/production.rb.erb +2 -2
  6. data/app/controllers/.gitignore +0 -0
  7. data/app/doc/.gitignore +0 -0
  8. data/app/helpers/.gitignore +0 -0
  9. data/app/lib/application.rb.erb +4 -2
  10. data/app/lib/tasks/.gitignore +0 -0
  11. data/app/log/.gitignore +0 -0
  12. data/app/models/.gitignore +0 -0
  13. data/app/public/css/.gitignore +0 -0
  14. data/app/public/flash/.gitignore +0 -0
  15. data/app/public/images/.gitignore +0 -0
  16. data/app/public/javascript/.gitignore +0 -0
  17. data/app/schema/migrations/.gitignore +0 -0
  18. data/app/tmp/sessions/.gitignore +0 -0
  19. data/app/views/.gitignore +0 -0
  20. data/bin/waves +29 -46
  21. data/bin/waves-console +1 -1
  22. data/bin/waves-server +1 -1
  23. data/lib/commands/waves-console.rb +0 -3
  24. data/lib/controllers/base.rb +11 -0
  25. data/lib/controllers/mixin.rb +39 -32
  26. data/lib/dispatchers/base.rb +37 -22
  27. data/lib/dispatchers/default.rb +25 -11
  28. data/lib/foundations/default.rb +6 -8
  29. data/lib/foundations/simple.rb +13 -0
  30. data/lib/helpers/asset_helper.rb +67 -0
  31. data/lib/helpers/common.rb +4 -0
  32. data/lib/helpers/default.rb +13 -0
  33. data/lib/helpers/form.rb +1 -0
  34. data/lib/helpers/number_helper.rb +25 -0
  35. data/lib/helpers/tag_helper.rb +58 -0
  36. data/lib/helpers/url_helper.rb +77 -0
  37. data/lib/layers/default_errors.rb +7 -5
  38. data/lib/layers/mvc.rb +54 -0
  39. data/lib/layers/orm/active_record.rb +93 -0
  40. data/lib/layers/orm/active_record/migrations/empty.rb.erb +9 -0
  41. data/lib/layers/orm/active_record/tasks/generate.rb +28 -0
  42. data/lib/layers/orm/active_record/tasks/schema.rb +22 -0
  43. data/lib/layers/orm/data_mapper.rb +38 -0
  44. data/lib/layers/orm/filebase.rb +22 -0
  45. data/lib/layers/orm/migration.rb +79 -0
  46. data/lib/layers/orm/sequel.rb +86 -0
  47. data/lib/layers/orm/sequel/migrations/empty.rb.erb +9 -0
  48. data/lib/layers/orm/sequel/tasks/generate.rb +28 -0
  49. data/lib/layers/orm/sequel/tasks/schema.rb +16 -0
  50. data/lib/layers/simple.rb +35 -0
  51. data/lib/layers/simple_errors.rb +11 -5
  52. data/lib/mapping/mapping.rb +61 -24
  53. data/lib/mapping/pretty_urls.rb +5 -3
  54. data/lib/renderers/erubis.rb +3 -1
  55. data/lib/renderers/mixin.rb +1 -4
  56. data/lib/runtime/application.rb +12 -8
  57. data/lib/runtime/blackboard.rb +57 -0
  58. data/lib/runtime/configuration.rb +60 -55
  59. data/lib/runtime/logger.rb +8 -1
  60. data/lib/runtime/request.rb +5 -4
  61. data/lib/runtime/response.rb +3 -3
  62. data/lib/runtime/response_mixin.rb +3 -0
  63. data/lib/runtime/response_proxy.rb +4 -1
  64. data/lib/runtime/server.rb +18 -5
  65. data/lib/runtime/session.rb +20 -10
  66. data/lib/tasks/cluster.rb +2 -1
  67. data/lib/tasks/generate.rb +76 -11
  68. data/lib/utilities/hash.rb +31 -0
  69. data/lib/utilities/inflect.rb +86 -170
  70. data/lib/utilities/inflect/english.rb +84 -0
  71. data/lib/utilities/integer.rb +23 -16
  72. data/lib/utilities/module.rb +19 -15
  73. data/lib/utilities/object.rb +22 -14
  74. data/lib/utilities/proc.rb +13 -6
  75. data/lib/utilities/string.rb +58 -44
  76. data/lib/utilities/symbol.rb +4 -1
  77. data/lib/views/base.rb +9 -0
  78. data/lib/views/mixin.rb +3 -1
  79. data/lib/waves.rb +15 -13
  80. metadata +50 -25
  81. data/lib/utilities/kernel.rb +0 -34
  82. data/lib/verify/mapping.rb +0 -29
  83. data/lib/verify/request.rb +0 -40
@@ -1,3 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
+ require 'rubygems'
2
3
  require 'startup'
3
4
  require 'commands/waves-console'
data/app/bin/waves-server CHANGED
@@ -1,3 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
+ require 'rubygems'
2
3
  require 'startup'
3
4
  require 'commands/waves-server'
@@ -1,13 +1,12 @@
1
- module <%= name %>
1
+ module <%= @name %>
2
2
 
3
3
  module Configurations
4
4
 
5
- class Development
5
+ class Development < Default
6
6
 
7
- database :host => 'localhost', :adapter => 'sqlite', :database => '<%= name.downcase %>',
8
- :user => 'root', :password => ''
7
+ database :adapter => 'sqlite', :database => '<%= @name.downcase %>'
9
8
 
10
- reloadable [ <%= name %> ]
9
+ reloadable [ <%= @name %> ]
11
10
 
12
11
  log :level => :debug
13
12
 
@@ -15,7 +14,7 @@ module <%= name %>
15
14
 
16
15
  port 3000
17
16
 
18
- handler ::Rack::Handler::Mongrel, :Host => host, :Port => port
17
+ handler ::Rack::Handler::Mongrel, :Host => host, :Port => port
19
18
  # handler ::Rack::Handler::WEBrick, :BindAddress => host, :Port => port
20
19
  # handler ::Rack::Handler::Thin, :Host => host, :Port => port
21
20
 
@@ -1,9 +1,8 @@
1
- module <%= name %>
1
+ module <%= @name %>
2
2
 
3
3
  module Configurations
4
4
 
5
5
  module Mapping
6
- extend Waves::Mapping
7
6
  # your custom rules go here
8
7
  include Waves::Mapping::PrettyUrls::RestRules
9
8
  include Waves::Mapping::PrettyUrls::GetRules
@@ -1,10 +1,10 @@
1
- module <%= name %>
1
+ module <%= @name %>
2
2
 
3
3
  module Configurations
4
4
 
5
5
  class Production < Default
6
6
 
7
- database :host => 'localhost', :adapter => 'mysql', :database => '<%= name.downcase %>',
7
+ database :host => 'localhost', :adapter => 'mysql', :database => '<%= @name.downcase %>',
8
8
  :user => 'root', :password => ''
9
9
 
10
10
  reloadable []
File without changes
File without changes
File without changes
@@ -1,3 +1,5 @@
1
- module <%= name %>
1
+ require 'layers/orm/sequel'
2
+ module <%= @name %>
2
3
  include Waves::Foundations::Default
3
- end
4
+ include Waves::Layers::ORM::Sequel
5
+ end
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
data/bin/waves CHANGED
@@ -1,10 +1,26 @@
1
1
  #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'choice'
4
+ require 'rakegen'
5
+
6
+ # if we're in the waves source, prepend it to the load path
7
+ waves_lib = File.expand_path( "#{File.dirname(__FILE__)}/../../waves/lib" )
8
+ $:.unshift waves_lib if File.exist?(waves_lib)
9
+ require 'waves'
10
+
11
+ begin
12
+ require 'utilities/string'
13
+ rescue LoadError
14
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'utilities', 'string')
15
+ end
2
16
 
3
- # rudimentary argument checks
4
- if ARGV.length != 1 || ARGV[0] == '--help'
5
- $stderr.puts "Usage: waves {app_name}"
6
- $stderr.puts "app_name may contain only letters, numbers, and underscores."
7
- exit 1
17
+ Choice.options do
18
+ banner 'Usage: waves path/to/app [-h]'
19
+ option :help do
20
+ long '--help'
21
+ desc 'Show this message'
22
+ end
23
+
8
24
  end
9
25
 
10
26
  app_path = ARGV[0]
@@ -16,51 +32,18 @@ if app_name =~ /[^\w\d_]/
16
32
  TEXT
17
33
  end
18
34
 
35
+ template = "#{WAVES}/app"
19
36
 
20
- require 'rubygems'
21
- require 'erubis'
22
- require 'extensions/all'
23
- begin
24
- require 'utilities/string'
25
- rescue LoadError
26
- require File.join(File.dirname(__FILE__), '..', 'lib', 'utilities', 'string')
37
+ generator = Rakegen.new("waves:app") do |gen|
38
+ gen.source = template
39
+ gen.target = app_path
40
+ gen.template_assigns = {:name => app_name.camel_case}
41
+ gen.executables = %w{ bin/waves-console bin/waves-server}
27
42
  end
28
43
 
29
- require 'fileutils'
30
- include FileUtils
31
-
32
- # are we calling this script from within the waves framework source?
33
- script_path = File.expand_path(__FILE__)
34
- WAVES_SRC = File.dirname(File.dirname(script_path))
35
- IN_WAVES_SRC = Dir.pwd.match(WAVES_SRC) ? true : false
36
-
37
-
38
44
  puts "** Creating new Waves application ..."
39
- template = begin
40
- File.join File.dirname(File.readlink(__FILE__)), '..', 'app'
41
- rescue Exception
42
- File.join( File.dirname(__FILE__),'..','app')
43
- end
44
- mkdir(app_path)
45
- cp_r Dir["#{template}/*"], app_path
46
45
 
47
- # get rid of placeholder files left over from gem install
48
- Dir["#{app_path}/**/EMPTY"].each { |path| rm path }
49
- # Dir["#{app_path}/**/EMPTY"].each { |path| system "rm #{path}" }
50
-
51
- # next, process all template files ...
52
- Dir["#{app_path}/**/*.erb"].each do |path|
53
- unless path =~ %r{^#{app_path}/(schema/migrations/templates|templates)}
54
- name = app_name.camel_case
55
- File.write( path.gsub(/\.erb$/,''),
56
- Erubis::Eruby.new( File.read( path ) ).result( binding ) )
57
- rm path
58
- end
59
- end
60
-
61
- unless RUBY_PLATFORM =~ /mswin32/
62
- # make the scripts executable
63
- system "chmod ug+x #{app_path}/bin/waves-*"
64
- end
46
+ Rake::Task["waves:app"].invoke
65
47
 
66
48
  puts "** Application created!"
49
+
data/bin/waves-console CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
+ require 'rubygems'
2
3
  require 'startup'
3
-
4
4
  require 'commands/waves-console'
data/bin/waves-server CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
+ require 'rubygems'
2
3
  require 'startup'
3
-
4
4
  require 'commands/waves-server'
@@ -15,9 +15,6 @@ end
15
15
 
16
16
  console = Waves::Console.load( Choice.choices )
17
17
  Object.send(:define_method, :waves) { console }
18
- Object.instance_eval do
19
- include Waves::Verify::Helpers::Request
20
- end
21
18
  require 'irb'
22
19
  require 'irb/completion'
23
20
  ARGV.clear
@@ -0,0 +1,11 @@
1
+ module Waves
2
+ module Controllers
3
+ class Base
4
+
5
+ include Waves::Controllers::Mixin
6
+
7
+ def attributes; params[model_name.singular.intern]; end
8
+
9
+ end
10
+ end
11
+ end
@@ -7,7 +7,7 @@ module Waves
7
7
  # a request. For example, a controller updates a model instance using
8
8
  # parameters from the request.
9
9
  #
10
- # Controller methods simply return data (a resource), if necessary, that
10
+ # Public controller methods simply return data (a resource), if necessary, that
11
11
  # can be then used by views to determine how to render that data.
12
12
  # Controllers do not determine which view will be invoked. They are
13
13
  # independent of the view; one controller method might be suitable for
@@ -76,13 +76,12 @@ module Waves
76
76
  module Controllers
77
77
 
78
78
  #
79
- # This mixin provides some handy methods for Waves controllers. You will probably
80
- # want to include it in any controllers you define for your application. The default
81
- # controllers generated using the +wave+ command already do this.
79
+ # Waves::Controllers::Mixin adapts a controller class for use in mappings and provides utility methods.
80
+ # It is included in controllers autocreated by the Default foundation, so you do not need to include
81
+ # it in subclasses of the same.
82
82
  #
83
- # Basically, what the mixin does is adapt the class so that it can be used within
84
- # mappings (see Waves::Mapping); add some simple reflection to allow controller methods
85
- # to be written generically (i.e., without reference to a specific model); and provide
83
+ # The utility methods include simple reflection to allow controller methods
84
+ # to be written generically (i.e., without reference to a specific model) and
86
85
  # parameter destructuring for the request parameters.
87
86
  #
88
87
 
@@ -92,18 +91,20 @@ module Waves
92
91
 
93
92
  include Waves::ResponseMixin
94
93
 
95
- # When you include this Mixin, a +process+ class method is added to your class,
96
- # which accepts a request object and a block. The request object is used to initialize
97
- # the controller and the block is evaluated using +instance_eval+. This allows the
98
- # controller to be used within a mapping file.
94
+ # When this mixin is included it adds a class method named +process+,
95
+ # which accepts a request object and a block. The +process+ method initializes the
96
+ # controller with the request, then evaluates the block using +instance_eval+. This allows the
97
+ # controller to be used from within a mapping lambda (i.e. a ResponseProxy).
99
98
 
100
- def self.included( c )
101
- def c.process( request, &block )
99
+ def self.included( mod )
100
+ def mod.process( request, &block )
102
101
  self.new( request ).instance_eval( &block )
103
102
  end
104
103
  end
105
104
 
106
- def initialize( request ); @request = request; end
105
+ def initialize( request )
106
+ @request = request
107
+ end
107
108
 
108
109
  # The params variable is taken from the request object and "destructured", so that
109
110
  # a parameter named 'blog.title' becomes:
@@ -111,16 +112,16 @@ module Waves
111
112
  # params['blog']['title']
112
113
  #
113
114
  # If you want to access the original parameters object, you can still do so using
114
- # +request.parameters+ instead of simply +params+.
115
+ # +request.params+ instead of simply +params+.
115
116
  def params; @params ||= destructure(request.params); end
116
117
 
117
- # You can access the name of the model related to this controller using this method.
118
- # It simply takes the basename of the module and converts it to snake case, so if the
119
- # model uses a different plurality, this won't, in fact, be the model name.
118
+ # Returns the name of the model corresponding to this controller by taking the basename
119
+ # of the module and converting it to snake case. If the model plurality is different than
120
+ # the controller, this will not, in fact, be the model name.
120
121
  def model_name; self.class.basename.snake_case; end
121
122
 
122
- # This uses the model_name method to attempt to identify the model corresponding to this
123
- # controller. This allows you to write generic controller methods such as:
123
+ # Returns the model corresponding to this controller by naively assuming that
124
+ # +model_name+ must be correct. This allows you to write generic controller methods such as:
124
125
  #
125
126
  # model.find( name )
126
127
  #
@@ -130,24 +131,30 @@ module Waves
130
131
 
131
132
  private
132
133
 
133
- def destructure(hash)
134
- rval = {}
135
- hash.keys.map{ |key|key.split('.') }.each do |keys|
136
- destructure_with_array_keys(hash,'',keys,rval)
134
+ def destructure( hash )
135
+ destructured = {}
136
+ hash.keys.map { |key| key.split('.') }.each do |keys|
137
+ destructure_with_array_keys(hash, '', keys, destructured)
137
138
  end
138
- rval
139
+ destructured
139
140
  end
140
141
 
141
- def destructure_with_array_keys(hash,prefix,keys,rval)
142
+ def destructure_with_array_keys( hash, prefix, keys, destructured )
142
143
  if keys.length == 1
143
- val = hash[prefix+keys.first]
144
- rval[keys.first.intern] = case val
145
- when String then val.strip
146
- when Hash then val
144
+ key = "#{prefix}#{keys.first}"
145
+ val = hash[key]
146
+ destructured[keys.first.intern] = case val
147
+ when String
148
+ val.strip
149
+ when Hash
150
+ val
151
+ when nil
152
+ raise key.inspect
147
153
  end
148
154
  else
149
- rval = ( rval[keys.first.intern] ||= {} )
150
- destructure_with_array_keys(hash,(keys.shift<<'.'),keys,rval)
155
+ destructured = ( destructured[keys.first.intern] ||= {} )
156
+ new_prefix = "#{prefix}#{keys.shift}."
157
+ destructure_with_array_keys( hash, new_prefix, keys, destructured )
151
158
  end
152
159
  end
153
160
 
@@ -2,8 +2,14 @@ module Waves
2
2
 
3
3
  module Dispatchers
4
4
 
5
+ # A NotFoundError means what you think it means. The dispatchers included with Waves do not
6
+ # natively intercept this exception. Instead an exception handler must be registered in the application
7
+ # mappings. The Simple foundation registers a minimal handler, while the Default foundation registers
8
+ # a slightly fleshier one.
5
9
  class NotFoundError < Exception ; end
6
10
 
11
+ # Redirect exceptions are rescued by the Waves dispatcher and used to set the
12
+ # response status and location.
7
13
  class Redirect < Exception
8
14
  attr_reader :path, :status
9
15
  def initialize( path, status = '302' )
@@ -12,38 +18,47 @@ module Waves
12
18
  end
13
19
  end
14
20
 
15
- # The Base dispatcher simply makes it easier to write dispatchers by inheriting
16
- # from it. It creates a Waves request, ensures the request processing is done
17
- # within a mutex, benchmarks the request processing, logs it, and handles common
18
- # exceptions and redirects. Derived classes need only process the request within
19
- # their +safe+ method, which takes a Waves::Request and returns a Waves::Response.
21
+ # Waves::Dispatchers::Base provides the basic request processing structure.
22
+ # All other Waves dispatchers should inherit from it. It creates a Waves request,
23
+ # determines whether to enclose the request processing in a mutex, benchmarks it,
24
+ # logs it, and handles common exceptions and redirects. Derived classes need only
25
+ # process the request within the +safe+ method, which must take a Waves::Request and return a Waves::Response.
20
26
 
21
27
  class Base
22
28
 
23
- # Like any Rack application, Waves' dispatchers must provide a call method
24
- # taking an +env+ parameter.
29
+ # As with any Rack application, a Waves dispatcher must provide a call method
30
+ # that takes an +env+ parameter.
25
31
  def call( env )
26
- Waves::Application.instance.synchronize do
27
- request = Waves::Request.new( env )
28
- response = request.response
29
- t = Benchmark.realtime do
30
- begin
31
- safe( request )
32
- rescue Dispatchers::Redirect => redirect
33
- response.status = redirect.status
34
- response.location = redirect.path
35
- end
36
- end
37
- Waves::Logger.info "#{request.method}: #{request.url} handled in #{(t*1000).round} ms."
38
- response.finish
32
+ if Waves.config.synchronize?
33
+ Waves::Application.instance.synchronize { _call( env ) }
34
+ else
35
+ _call( env )
39
36
  end
40
37
  end
41
38
 
42
- # Called by event driven servers like thin and ebb. Return true if
43
- # the server should run the request in a separate thread.
39
+ # Called by event driven servers like thin and ebb. Returns true if
40
+ # the server should run the request in a separate thread, as determined by
41
+ # Configurations::Mapping#threaded?
44
42
  def deferred?( env )
45
43
  Waves::Application.instance.mapping.threaded?( env )
46
44
  end
45
+
46
+ private
47
+
48
+ def _call( env )
49
+ request = Waves::Request.new( env )
50
+ response = request.response
51
+ t = Benchmark.realtime do
52
+ begin
53
+ safe( request )
54
+ rescue Dispatchers::Redirect => redirect
55
+ response.status = redirect.status
56
+ response.location = redirect.path
57
+ end
58
+ end
59
+ Waves::Logger.info "#{request.method}: #{request.url} handled in #{(t*1000).round} ms."
60
+ response.finish
61
+ end
47
62
 
48
63
  end
49
64
 
@@ -3,13 +3,14 @@ module Waves
3
3
  module Dispatchers
4
4
 
5
5
  #
6
- # The default dispatcher essentially checks the application's mapping to see
7
- # what to do with the request URL. It checks before and after filters (wrap
8
- # filters are just a combination of both) as well as path and url mappings.
6
+ # Waves::Dispatchers::Default matches requests against an application's mappings to
7
+ # determine what main action to take, as well as what before, after, always, and exception-handling
8
+ # blocks must run.
9
9
  #
10
10
  # The default dispatcher also attempts to set the content type based on the
11
- # MIME type implied by any file extension used in the request URL using Mongrel's
12
- # MIME types YAML file.
11
+ # file extension used in the request URL. It does this using the class defined in
12
+ # the current configuration's +mime_types+ attribute, which defaults to Mongrel's
13
+ # MIME type handler.
13
14
  #
14
15
  # You can write your own dispatcher and use it in your application's configuration
15
16
  # file. By default, they use the default dispatcher, like this:
@@ -27,13 +28,14 @@ module Waves
27
28
  # Takes a Waves::Request and returns a Waves::Reponse
28
29
  def safe( request )
29
30
 
30
- begin
31
- response = request.response
31
+ response = request.response
32
+
33
+ Waves::Application.instance.reload if Waves::Application.instance.debug?
34
+ response.content_type = Waves::Application.instance.config.mime_types[ request.path ] || 'text/html'
32
35
 
33
- Waves::Application.instance.reload if Waves::Application.instance.debug?
34
- response.content_type = Waves::Application.instance.config.mime_types[ request.path ] || 'text/html'
36
+ mapping = Waves::Application.instance.mapping[ request ]
35
37
 
36
- mapping = Waves::Application.instance.mapping[ request ]
38
+ begin
37
39
 
38
40
  mapping[:before].each do | block, args |
39
41
  ResponseProxy.new(request).instance_exec(*args,&block)
@@ -43,11 +45,13 @@ module Waves
43
45
 
44
46
  block, args = mapping[:action]
45
47
  response.write( ResponseProxy.new(request).instance_exec(*args, &block) )
46
-
48
+
47
49
  mapping[:after].each do | block, args |
48
50
  ResponseProxy.new(request).instance_exec(*args,&block)
49
51
  end
52
+
50
53
  rescue Exception => e
54
+
51
55
  handler = mapping[:handlers].detect do | exception, block, args |
52
56
  e.is_a? exception
53
57
  end
@@ -56,6 +60,16 @@ module Waves
56
60
  else
57
61
  raise e
58
62
  end
63
+
64
+ ensure
65
+ mapping[:always].each do | block, args |
66
+ begin
67
+ ResponseProxy.new(request).instance_exec(*args,&block)
68
+ rescue Exception => e
69
+ Waves::Logger.info e.to_s
70
+ end
71
+ end
72
+
59
73
  end
60
74
 
61
75
  end