dyoder-waves 0.7.3 → 0.7.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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