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
@@ -0,0 +1,9 @@
1
+ class <%= @class_name %> < ActiveRecord::Migration
2
+
3
+ def self.up
4
+ end
5
+
6
+ def self.down
7
+ end
8
+
9
+ end
@@ -0,0 +1,28 @@
1
+ namespace :generate do
2
+
3
+ desc 'Generate an ActiveRecord model, with name=<name>'
4
+ task :model do |task|
5
+ name = ENV['name']
6
+ model_name = name.camel_case
7
+ raise "Cannot generate Default yet" if model_name == 'Default'
8
+
9
+ filename = File.expand_path "models/#{name}.rb"
10
+ if File.exist?(filename)
11
+ $stderr.puts "#{filename} already exists"
12
+ exit
13
+ end
14
+
15
+ model = <<TEXT
16
+ module #{Waves.application.name}
17
+ module Models
18
+ class #{model_name} < Default
19
+
20
+ end
21
+ end
22
+ end
23
+ TEXT
24
+
25
+ File.write( filename, model )
26
+ end
27
+
28
+ end
@@ -0,0 +1,22 @@
1
+ require "#{File.dirname(__FILE__)}/../../migration"
2
+
3
+ namespace :schema do
4
+
5
+ desc "Create ActiveRecord migration with name=<name>"
6
+ task :migration do |task|
7
+ Waves::Layers::ORM.create_migration_for(ActiveRecord)
8
+ end
9
+
10
+ desc "Performs ActiveRecord migrations to version=<version>"
11
+ task :migrate => :connect do |task|
12
+ version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
13
+ ActiveRecord::Migrator.migrate(Waves::Layers::ORM.migration_directory, version)
14
+ end
15
+
16
+ task :connect do
17
+ Waves.application.database
18
+ ActiveRecord::Base.logger = Logger.new($stdout)
19
+ end
20
+
21
+ end
22
+
@@ -0,0 +1,38 @@
1
+ gem 'dm-core', '=0.9.0'
2
+
3
+ require 'data_mapper'
4
+
5
+ module Waves
6
+ module Layers
7
+ module ORM
8
+
9
+ # Work in Progress
10
+ module DataMapper
11
+
12
+ def self.included(app)
13
+
14
+ def app.database
15
+ @adapter ||= ::DataMapper.setup(:main_repository, config.database[:database])
16
+ end
17
+
18
+ app.auto_eval :Models do
19
+ auto_load true, :directories => [:models]
20
+ end
21
+
22
+ app.auto_eval :Configurations do
23
+ auto_eval :Mapping do
24
+ before true do
25
+ app.database #force adapter init if not already done
26
+ ::DataMapper::Repository.context.push(::DataMapper::Repository.new(:main_repository))
27
+ end
28
+ always true do
29
+ ::DataMapper::Repository.context.pop
30
+ end
31
+ end
32
+ end
33
+
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,22 @@
1
+ module Waves
2
+ module Layers
3
+ module ORM
4
+
5
+ # Work in Progress
6
+ module Filebase
7
+
8
+ def self.included(app)
9
+ app.module_eval do
10
+ auto_eval( :Models ) do
11
+ auto_eval( true ) { include Filebase::Model[ :db / self.name.snake_case ] }
12
+ end
13
+ end
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,79 @@
1
+ module Waves
2
+ module Layers
3
+
4
+ # Helper methods to establish inter-ORM standards
5
+ module ORM
6
+
7
+ # Glob pattern
8
+ MIGRATION_FILE_PATTERN = '[0-9][0-9][0-9]_*.rb'.freeze
9
+
10
+ def self.create_migration_for(orm)
11
+ source = migration_template(orm.to_s.snake_case, ENV['template'])
12
+ destination = migration_destination(ENV['name'])
13
+ migration_name = migration_name(ENV['name'])
14
+
15
+ context = {:class_name => migration_name.camel_case}
16
+
17
+ write_migration(context, source, destination)
18
+ end
19
+
20
+ # Where Waves keeps its migration files
21
+ def self.migration_directory
22
+ :schema / :migrations
23
+ end
24
+
25
+ # Returns any found migration files in the supplied directory.
26
+ def self.migration_files(range = nil)
27
+ pattern = migration_directory / MIGRATION_FILE_PATTERN
28
+ files = Dir[pattern].inject([]) do |m, path|
29
+ m[File.basename(path).to_i] = path
30
+ m
31
+ end
32
+ filtered = range ? files[range] : files
33
+ filtered ? filtered.compact : []
34
+ end
35
+
36
+ # Use the supplied version number or determine the next in sequence
37
+ # based on the migration files in the migration directory
38
+ def self.next_migration_version
39
+ version = ENV['version'] || latest_migration_version
40
+ version.to_i + 1
41
+ end
42
+
43
+ # Uses the migration files in the migration directory to determine
44
+ # the highest numbered existing migration.
45
+ def self.latest_migration_version
46
+ l = migration_files.last
47
+ l ? File.basename(l).to_i : nil
48
+ end
49
+
50
+ # If the user doesn't pass a name, defaults to "migration"
51
+ def self.migration_name(name=nil)
52
+ name || 'migration'
53
+ end
54
+
55
+ # Returns the path to the migration template file for the given ORM.
56
+ # <em>orm</em> can be a symbol or string
57
+ def self.migration_template(orm, name=nil)
58
+ file = ( name || 'empty' ) + '.rb.erb'
59
+ source = File.dirname(__FILE__) / orm / :migrations / file
60
+ end
61
+
62
+ # Given a migration name, returns the path of the file that would be created.
63
+ def self.migration_destination(name)
64
+ version = next_migration_version
65
+ migration_directory / "#{'%03d' % version}_#{migration_name(name)}.rb"
66
+ end
67
+
68
+ # Takes an assigns hash as the Erubis context. Keys in the hash become
69
+ # instance variable names.
70
+ def self.write_migration(context, source, destination)
71
+ code = Erubis::Eruby.new( File.read( source ) ).evaluate( context )
72
+ puts "Creating #{destination}"
73
+ File.write( destination, code )
74
+ end
75
+
76
+ end
77
+ end
78
+
79
+ end
@@ -0,0 +1,86 @@
1
+ gem 'sequel', '>= 2.0.0'
2
+ require 'sequel'
3
+ require "#{File.dirname(__FILE__)}/sequel/tasks/schema" if defined?(Rake)
4
+ require "#{File.dirname(__FILE__)}/sequel/tasks/generate" if defined?(Rake)
5
+
6
+ module Waves
7
+ module Layers
8
+ module ORM # :nodoc:
9
+
10
+ # Sets up the Sequel connection and configures AutoCode on Models, so that constants in that
11
+ # namespace get loaded from file or created as subclasses of Models::Default
12
+ module Sequel
13
+
14
+ # On inclusion, this module:
15
+ # - creates on the application module a database method that establishes the Sequel connection
16
+ # - arranges for autoloading/autocreation of missing constants in the Models namespace
17
+ # - defines Sequel-specific helper methods on Waves::Controllers::Base
18
+ #
19
+ # The controller helper methdods are:
20
+ # - all
21
+ # - find(name)
22
+ # - create
23
+ # - delete(name)
24
+ # - update(name)
25
+ #
26
+ def self.included(app)
27
+
28
+ def app.database ; @sequel ||= ::Sequel.open( config.database ) ; end
29
+
30
+ app.auto_create_module( :Models ) do
31
+ include AutoCode
32
+ auto_create_class :Default, ::Sequel::Model
33
+ auto_load :Default, :directories => [ :models ]
34
+ end
35
+
36
+ app.auto_eval :Models do
37
+ auto_create_class true, app::Models::Default
38
+ auto_load true, :directories => [ :models ]
39
+ # set the Sequel dataset based on the model class name
40
+ # note that this is not done for app::Models::Default, as it isn't
41
+ # supposed to represent a table
42
+ auto_eval true do
43
+ default = superclass.basename.snake_case.pluralize.intern
44
+ if @dataset && @dataset.opts[:from] != [ default ]
45
+ # don't clobber dataset from autoloaded file
46
+ else
47
+ set_dataset Waves.application.database[ basename.snake_case.pluralize.intern ]
48
+ end
49
+ end
50
+ end
51
+
52
+ Waves::Controllers::Base.instance_eval do
53
+ include Waves::Layers::ORM::Sequel::ControllerMethods
54
+ end
55
+
56
+ end
57
+
58
+ # Mixed into Waves::Controllers::Base. Provides ORM-specific helper methods for model access.
59
+ module ControllerMethods
60
+ def all
61
+ model.all
62
+ end
63
+
64
+ def find( name )
65
+ model[ :name => name ] or not_found
66
+ end
67
+
68
+ def create
69
+ model.create( attributes )
70
+ end
71
+
72
+ def delete( name )
73
+ find( name ).destroy
74
+ end
75
+
76
+ def update( name )
77
+ instance = find( name )
78
+ instance.update_with_params( attributes )
79
+ instance
80
+ end
81
+ end
82
+
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,9 @@
1
+ class <%= @class_name %> < Sequel::Migration
2
+
3
+ def up
4
+ end
5
+
6
+ def down
7
+ end
8
+
9
+ end
@@ -0,0 +1,28 @@
1
+ namespace :generate do
2
+
3
+ desc 'Generate a Sequel model, with name=<name>'
4
+ task :model do |task|
5
+ name = ENV['name']
6
+ model_name = name.camel_case
7
+ raise "Cannot generate Default yet" if model_name == 'Default'
8
+
9
+ filename = File.expand_path "models/#{name}.rb"
10
+ if File.exist?(filename)
11
+ $stderr.puts "#{filename} already exists"
12
+ exit
13
+ end
14
+
15
+ model = <<TEXT
16
+ module #{Waves.application.name}
17
+ module Models
18
+ class #{model_name} < Default
19
+
20
+ end
21
+ end
22
+ end
23
+ TEXT
24
+
25
+ File.write( filename, model )
26
+ end
27
+
28
+ end
@@ -0,0 +1,16 @@
1
+ require "#{File.dirname(__FILE__)}/../../migration"
2
+
3
+ namespace :schema do
4
+
5
+ desc "Create a new Sequel Migration with name=<name>"
6
+ task :migration do |task|
7
+ Waves::Layers::ORM.create_migration_for(Sequel)
8
+ end
9
+
10
+ desc "Performs Sequel migrations to version=<version>"
11
+ task :migrate do |task|
12
+ version = ENV['version']; version = version.to_i unless version.nil?
13
+ Sequel::Migrator.apply( Waves.application.database, Waves::Layers::ORM.migration_directory , version )
14
+ end
15
+
16
+ end
@@ -0,0 +1,35 @@
1
+ module Waves
2
+
3
+ # Waves uses Layers to provide discrete, stackable, interchangeable bundles of functionality.
4
+ #
5
+ # Developers can make use of Layers by including them directly in a Waves application:
6
+ #
7
+ # module MyApp
8
+ # include SomeLayer
9
+ # end
10
+ module Layers
11
+
12
+ # Creates the Configurations namespace and establishes the standard autoload-or-autocreate
13
+ # rules.
14
+ module Simple
15
+ def self.included( app )
16
+
17
+ def app.config ; Waves.config ; end
18
+ def app.configurations ; self::Configurations ; end
19
+
20
+ app.instance_eval { include AutoCode }
21
+
22
+ app.auto_create_module( :Configurations ) do
23
+ include AutoCode
24
+ auto_create_class true, Waves::Configurations::Default
25
+ auto_load :Mapping, :directories => [:configurations]
26
+ auto_load true, :directories => [:configurations]
27
+ auto_eval :Mapping do
28
+ extend Waves::Mapping
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
@@ -1,16 +1,22 @@
1
1
  module Waves
2
2
  module Layers
3
+ # Configures Waves for minimal exception handling.
4
+ #
5
+ # For example,
6
+ # a NotFoundError results in response status of 404, with body text
7
+ # of "404 Not Found".
3
8
  module SimpleErrors
4
9
 
5
10
  def self.included( app )
6
11
 
7
- app.instance_eval do
8
-
9
- autoinit 'Configurations::Mapping' do
10
- handle(Waves::Dispatchers::NotFoundError) { response.status = 404 }
12
+ app.auto_eval :Configurations do
13
+ auto_eval :Mapping do
14
+ handle(Waves::Dispatchers::NotFoundError) do
15
+ response.status = 404; response.body = "404 Not Found"
16
+ end
11
17
  end
12
-
13
18
  end
19
+
14
20
  end
15
21
  end
16
22
  end
@@ -1,11 +1,33 @@
1
1
  module Waves
2
2
 
3
- # Waves::Mapping is a mixin for defining Waves URI mappings (mapping a request to Ruby code).
4
- # Mappings can work against the request url, path, and elements of the request (such as the
5
- # request method or accept header). Mappings may also include before, after, or wrap filters
6
- # to be run if they match the request. Mappings are created using an appropriate mapping method
7
- # along with a URL pattern (a string or regular expression), a hash of constraint options, and
8
- # a block, which is the code to run if the pattern matches.
3
+ # Mappings in Waves are the interface between the request dispatcher and your
4
+ # application code. The dispatcher matches each request against the mappings
5
+ # to determine a primary action and to collect sets of before, after, wrap,
6
+ # and always actions. The dispatcher also looks for an exception handler
7
+ # registered in the mappings when attempting a rescue.
8
+ #
9
+ # Each mapping associates a block with a set of constraints. Mappings can be
10
+ # one of several types:
11
+ #
12
+ # - action (the actual request processing and response)
13
+ # - handle (exception handling)
14
+ # - before
15
+ # - after
16
+ # - wrap (registers its block as both a before and after action)
17
+ # - always (like an "ensure" clause in a rescue)
18
+ #
19
+ # Actions are registered using path, url, or map. The other types may be
20
+ # registered using methods named after the type.
21
+ #
22
+ #
23
+ # The available constraints are:
24
+ #
25
+ # - a string or regexp that the path or url must match
26
+ # - parameters to match against the HTTP request headers and the Rack-specific variables (e.g. 'rack.url_scheme')
27
+ # - an additional hash reserved for settings not related to the Rack request (e.g. giving Rack handers special instructions for certain requests. See threaded? )
28
+ #
29
+ # The dispatcher evaluates mapping blocks in an instance of ResponseProxy,
30
+ # which provides access to foundational classes of a Waves application (i.e. controllers and views)
9
31
  #
10
32
  # == Examples
11
33
  #
@@ -104,6 +126,7 @@ module Waves
104
126
  end
105
127
 
106
128
  # Similar to before, except it runs its actions after any matching +url+ or +path+ actions.
129
+ # Note that after methods will run even if an exception is thrown during processing.
107
130
  def after( path, options = {}, &block )
108
131
  if path.is_a? Hash
109
132
  options = path
@@ -124,15 +147,26 @@ module Waves
124
147
  filters[:after] << [ options, block ]
125
148
  end
126
149
 
127
- # Maps a request to a block. Don't use this method directly unless you know what
128
- # you're doing. Use +path+ or +url+ instead.
129
- def map( path, options = {}, params = {}, &block )
150
+ # Like after, but will run even when an exception is thrown. Exceptions in
151
+ # always mappings are simply logged and ignored.
152
+ def always( path, options = {}, &block )
130
153
  if path.is_a? Hash
131
- params = options
132
154
  options = path
133
155
  else
134
156
  options[:path] = path
135
157
  end
158
+ filters[:always] << [ options, block ]
159
+ end
160
+
161
+ # Maps a request to a block. Don't use this method directly unless you know what
162
+ # you're doing. Use +path+ or +url+ instead.
163
+ def map( path, options = {}, params = {}, &block )
164
+ case path
165
+ when Hash
166
+ params = options; options = path
167
+ when String
168
+ options[:path] = path
169
+ end
136
170
  mapping << [ options, params, block ]
137
171
  end
138
172
 
@@ -155,7 +189,7 @@ module Waves
155
189
  def root( options = {}, params = {}, &block )
156
190
  path( %r{^/?$}, options, params, &block )
157
191
  end
158
-
192
+
159
193
  # Maps an exception handler to a block.
160
194
  def handle(exception, options = {}, &block )
161
195
  handlers << [exception,options, block]
@@ -186,12 +220,12 @@ module Waves
186
220
  end
187
221
  return false
188
222
  end
189
-
223
+
190
224
  # Match the given request against the defined rules. This is typically only called
191
225
  # by a dispatcher object, so you shouldn't typically use it directly.
192
226
  def []( request )
193
227
 
194
- rx = { :before => [], :after => [], :action => nil, :handlers => [] }
228
+ rx = { :before => [], :after => [], :always => [], :action => nil, :handlers => [] }
195
229
 
196
230
  ( filters[:before] + filters[:wrap] ).each do | options, function |
197
231
  matches = match( request, options, function )
@@ -208,6 +242,11 @@ module Waves
208
242
  rx[:after] << matches if matches
209
243
  end
210
244
 
245
+ filters[:always].each do | options, function |
246
+ matches = match( request, options, function )
247
+ rx[:always] << matches if matches
248
+ end
249
+
211
250
  handlers.each do | exception, options, function |
212
251
  matches = match( request, options, function )
213
252
  rx[:handlers] << matches.unshift(exception) if matches
@@ -218,35 +257,33 @@ module Waves
218
257
 
219
258
  # Clear all mapping rules
220
259
  def clear
221
- @mapping = @filters = nil;
260
+ @mapping = @filters = @handlers = nil;
222
261
  end
223
262
 
224
263
  private
225
264
 
226
265
  def mapping; @mapping ||= []; end
227
266
 
228
- def filters; @filters ||= { :before => [], :after => [], :wrap => [] }; end
267
+ def filters; @filters ||= { :before => [], :after => [], :wrap => [], :always => [] }; end
229
268
 
230
269
  def handlers; @handlers ||= []; end
231
270
 
232
271
  def match ( request, options, function )
233
272
  return nil unless satisfy( request, options )
234
- if options[:path]
235
- matches = options[:path].match( request.path )
236
- elsif options[:url]
237
- matches = options[:url].match( request.url )
238
- end
273
+ return [ function, nil ] if ( options[:path] == true or options[:url] == true )
274
+ matches = options[:path].match( request.path ) if options[:path]
275
+ matches = options[:url].match( request.url ) if options[:url]
239
276
  return [ function, matches ? matches[1..-1] : nil ]
240
277
  end
241
278
 
242
279
  def satisfy( request, options )
243
280
  options.nil? or options.all? do |name,wanted|
244
- got = request.send( name ) rescue request.env[ ( name =~ /^rack\./ ) ?
245
- name.to_s.downcase : name.to_s.upcase ]
246
- ( ( wanted.is_a?(Regexp) && wanted.match( got.to_s ) ) or
247
- got.to_s == wanted.to_s ) unless ( wanted.nil? or got.nil? )
281
+ return true if wanted == true
282
+ got = request.send( name ) rescue request.env[ ( name =~ /^rack\./ ) ? name.to_s.downcase : name.to_s.upcase ]
283
+ ( ( wanted.is_a?(Regexp) and wanted.match( got.to_s ) ) or got.to_s == wanted.to_s ) unless ( wanted.nil? or got.nil? )
248
284
  end
249
285
  end
250
286
  end
287
+
251
288
 
252
289
  end