waves-stable 0.7.7 → 2009.3.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (236) hide show
  1. data/bin/waves +20 -74
  2. data/doc/HISTORY +1 -44
  3. data/doc/README +1 -0
  4. data/doc/VERSION +1 -0
  5. data/lib/caches/file.rb +48 -0
  6. data/lib/caches/memcached.rb +40 -0
  7. data/lib/caches/simple.rb +25 -0
  8. data/lib/caches/synchronized.rb +25 -0
  9. data/lib/commands/console.rb +35 -0
  10. data/lib/commands/generate.rb +52 -0
  11. data/lib/commands/help.rb +5 -0
  12. data/lib/commands/{waves-server.rb → server.rb} +16 -3
  13. data/lib/dispatchers/base.rb +22 -21
  14. data/lib/dispatchers/default.rb +10 -66
  15. data/lib/ext/float.rb +13 -0
  16. data/lib/{utilities → ext}/hash.rb +2 -2
  17. data/lib/{utilities → ext}/integer.rb +5 -2
  18. data/lib/ext/kernel.rb +20 -0
  19. data/lib/ext/module.rb +20 -0
  20. data/lib/{utilities → ext}/object.rb +3 -5
  21. data/lib/ext/string.rb +20 -0
  22. data/lib/ext/symbol.rb +11 -0
  23. data/lib/ext/tempfile.rb +5 -0
  24. data/lib/foundations/classic.rb +66 -0
  25. data/lib/foundations/compact.rb +50 -0
  26. data/lib/helpers/basic.rb +11 -0
  27. data/lib/helpers/doc_type.rb +34 -0
  28. data/lib/helpers/extended.rb +21 -0
  29. data/lib/helpers/form.rb +3 -1
  30. data/lib/helpers/formatting.rb +3 -3
  31. data/lib/helpers/layouts.rb +37 -0
  32. data/lib/helpers/model.rb +8 -4
  33. data/lib/helpers/view.rb +2 -4
  34. data/lib/layers/inflect/english.rb +48 -5
  35. data/lib/layers/mvc.rb +18 -18
  36. data/lib/layers/mvc/controllers.rb +41 -0
  37. data/lib/layers/mvc/extensions.rb +52 -0
  38. data/lib/layers/orm/migration.rb +1 -1
  39. data/lib/layers/orm/{active_record.rb → providers/active_record.rb} +9 -14
  40. data/lib/layers/orm/{active_record → providers/active_record}/migrations/empty.rb.erb +0 -0
  41. data/lib/layers/orm/{active_record → providers/active_record}/tasks/generate.rb +1 -1
  42. data/lib/layers/orm/{active_record → providers/active_record}/tasks/schema.rb +1 -1
  43. data/lib/layers/orm/{data_mapper.rb → providers/data_mapper.rb} +3 -4
  44. data/lib/layers/orm/providers/filebase.rb +25 -0
  45. data/lib/layers/orm/{sequel.rb → providers/sequel.rb} +11 -7
  46. data/lib/layers/orm/{sequel → providers/sequel}/migrations/empty.rb.erb +0 -0
  47. data/lib/layers/orm/{sequel → providers/sequel}/tasks/generate.rb +5 -3
  48. data/lib/layers/orm/{sequel → providers/sequel}/tasks/schema.rb +2 -2
  49. data/lib/{renderers → layers/renderers}/erubis.rb +13 -16
  50. data/lib/{renderers → layers/renderers}/haml.rb +27 -25
  51. data/lib/layers/renderers/markaby.rb +29 -0
  52. data/lib/matchers/accept.rb +21 -0
  53. data/lib/matchers/base.rb +30 -0
  54. data/lib/matchers/content_type.rb +17 -0
  55. data/lib/matchers/path.rb +67 -0
  56. data/lib/matchers/query.rb +21 -0
  57. data/lib/matchers/request.rb +27 -0
  58. data/lib/matchers/resource.rb +19 -0
  59. data/lib/matchers/traits.rb +19 -0
  60. data/lib/matchers/uri.rb +20 -0
  61. data/lib/renderers/mixin.rb +15 -29
  62. data/lib/resources/mixin.rb +134 -0
  63. data/lib/resources/paths.rb +34 -0
  64. data/lib/runtime/configuration.rb +51 -136
  65. data/lib/runtime/console.rb +4 -1
  66. data/lib/runtime/logger.rb +24 -48
  67. data/lib/runtime/mime_types.rb +516 -2
  68. data/lib/runtime/mocks.rb +14 -0
  69. data/lib/runtime/monitor.rb +32 -0
  70. data/lib/runtime/request.rb +107 -39
  71. data/lib/runtime/response.rb +5 -2
  72. data/lib/runtime/response_mixin.rb +41 -22
  73. data/lib/runtime/runtime.rb +67 -0
  74. data/lib/runtime/server.rb +14 -101
  75. data/lib/runtime/session.rb +4 -43
  76. data/lib/runtime/worker.rb +86 -0
  77. data/lib/servers/base.rb +42 -0
  78. data/lib/servers/mongrel.rb +13 -0
  79. data/lib/servers/webrick.rb +13 -0
  80. data/lib/tasks/gem.rb +1 -0
  81. data/lib/tasks/generate.rb +67 -62
  82. data/lib/views/errors.rb +49 -0
  83. data/lib/views/mixin.rb +34 -82
  84. data/lib/waves.rb +36 -57
  85. data/samples/blog/Rakefile +16 -5
  86. data/samples/blog/configurations/default.rb +11 -0
  87. data/samples/blog/configurations/development.rb +9 -11
  88. data/samples/blog/configurations/production.rb +11 -15
  89. data/samples/blog/models/comment.rb +9 -0
  90. data/samples/blog/models/entry.rb +17 -0
  91. data/samples/blog/public/css/site.css +13 -2
  92. data/samples/blog/public/javascript/jquery-1.2.6.min.js +32 -0
  93. data/samples/blog/resources/entry.rb +39 -0
  94. data/samples/blog/resources/map.rb +9 -0
  95. data/samples/blog/schema/migrations/001_initial_schema.rb +3 -3
  96. data/samples/blog/schema/migrations/002_add_comments.rb +2 -2
  97. data/samples/blog/startup.rb +8 -6
  98. data/samples/blog/templates/comment/add.mab +6 -4
  99. data/samples/blog/templates/comment/list.mab +4 -4
  100. data/samples/blog/templates/entry/{editor.mab → edit.mab} +7 -6
  101. data/samples/blog/templates/entry/list.mab +10 -5
  102. data/samples/blog/templates/entry/show.mab +16 -7
  103. data/samples/blog/templates/entry/summary.mab +8 -4
  104. data/samples/blog/templates/errors/not_found_404.mab +6 -1
  105. data/samples/blog/templates/layouts/default.mab +5 -3
  106. data/samples/blog/templates/waves/status.mab +85 -0
  107. data/{app → templates/classic}/Rakefile +21 -3
  108. data/templates/classic/configurations/default.rb.erb +9 -0
  109. data/{app → templates/classic}/configurations/development.rb.erb +3 -8
  110. data/{app → templates/classic}/configurations/production.rb.erb +3 -5
  111. data/{app → templates/classic}/controllers/.gitignore +0 -0
  112. data/{app/doc → templates/classic/helpers}/.gitignore +0 -0
  113. data/{app/helpers → templates/classic/lib/tasks}/.gitignore +0 -0
  114. data/{app/lib/tasks → templates/classic/models}/.gitignore +0 -0
  115. data/{app/log → templates/classic/public/css}/.gitignore +0 -0
  116. data/{app/models → templates/classic/public/flash}/.gitignore +0 -0
  117. data/{app/public/css → templates/classic/public/images}/.gitignore +0 -0
  118. data/{app/public/flash → templates/classic/public/javascript}/.gitignore +0 -0
  119. data/{app/public/images → templates/classic/resources}/.gitignore +0 -0
  120. data/templates/classic/resources/map.rb.erb +8 -0
  121. data/{app/public/javascript → templates/classic/schema/migrations}/.gitignore +0 -0
  122. data/templates/classic/startup.rb.erb +11 -0
  123. data/templates/classic/templates/errors/not_found_404.mab +7 -0
  124. data/templates/classic/templates/errors/server_error_500.mab +7 -0
  125. data/{app → templates/classic}/templates/layouts/default.mab +0 -0
  126. data/{app/schema/migrations → templates/classic/tmp/sessions}/.gitignore +0 -0
  127. data/{app/tmp/sessions → templates/classic/views}/.gitignore +0 -0
  128. data/templates/compact/startup.rb.erb +11 -0
  129. metadata +152 -258
  130. data/app/bin/waves-console +0 -4
  131. data/app/bin/waves-server +0 -4
  132. data/app/configurations/mapping.rb.erb +0 -14
  133. data/app/lib/application.rb.erb +0 -5
  134. data/app/startup.rb +0 -5
  135. data/app/templates/errors/not_found_404.mab +0 -2
  136. data/app/templates/errors/server_error_500.mab +0 -2
  137. data/app/views/.gitignore +0 -0
  138. data/bin/waves-console +0 -4
  139. data/bin/waves-server +0 -4
  140. data/lib/commands/waves-console.rb +0 -21
  141. data/lib/controllers/base.rb +0 -11
  142. data/lib/controllers/mixin.rb +0 -165
  143. data/lib/foundations/default.rb +0 -27
  144. data/lib/foundations/simple.rb +0 -30
  145. data/lib/helpers/asset_helper.rb +0 -67
  146. data/lib/helpers/common.rb +0 -66
  147. data/lib/helpers/default.rb +0 -13
  148. data/lib/helpers/number_helper.rb +0 -25
  149. data/lib/helpers/tag_helper.rb +0 -58
  150. data/lib/helpers/url_helper.rb +0 -77
  151. data/lib/layers/default_errors.rb +0 -26
  152. data/lib/layers/inflect/english/rules.rb +0 -88
  153. data/lib/layers/inflect/english/string.rb +0 -24
  154. data/lib/layers/orm/filebase.rb +0 -22
  155. data/lib/layers/simple.rb +0 -32
  156. data/lib/layers/simple_errors.rb +0 -23
  157. data/lib/mapping/mapping.rb +0 -289
  158. data/lib/mapping/pretty_urls.rb +0 -96
  159. data/lib/renderers/markaby.rb +0 -33
  160. data/lib/runtime/application.rb +0 -69
  161. data/lib/runtime/blackboard.rb +0 -57
  162. data/lib/runtime/debugger.rb +0 -9
  163. data/lib/runtime/response_proxy.rb +0 -30
  164. data/lib/tasks/cluster.rb +0 -26
  165. data/lib/utilities/inflect.rb +0 -110
  166. data/lib/utilities/module.rb +0 -21
  167. data/lib/utilities/proc.rb +0 -16
  168. data/lib/utilities/string.rb +0 -49
  169. data/lib/utilities/symbol.rb +0 -10
  170. data/lib/utilities/tempfile.rb +0 -9
  171. data/lib/views/base.rb +0 -9
  172. data/samples/blog/bin/waves-console +0 -3
  173. data/samples/blog/bin/waves-server +0 -3
  174. data/samples/blog/configurations/mapping.rb +0 -23
  175. data/samples/blog/doc/EMTPY +0 -0
  176. data/samples/blog/lib/application.rb +0 -5
  177. data/verify/app_generation/helpers.rb +0 -24
  178. data/verify/app_generation/startup.rb +0 -39
  179. data/verify/blackboard/blackboard_verify.rb +0 -92
  180. data/verify/blackboard/helpers.rb +0 -5
  181. data/verify/configurations/attributes.rb +0 -37
  182. data/verify/configurations/helpers.rb +0 -1
  183. data/verify/configurations/rack_integration.rb +0 -29
  184. data/verify/controllers/base.rb +0 -37
  185. data/verify/controllers/helpers.rb +0 -13
  186. data/verify/controllers/interface.rb +0 -51
  187. data/verify/core/helpers.rb +0 -3
  188. data/verify/core/utilities.rb +0 -177
  189. data/verify/foundations/default.rb +0 -86
  190. data/verify/foundations/default_application/Rakefile +0 -14
  191. data/verify/foundations/default_application/bin/waves-console +0 -3
  192. data/verify/foundations/default_application/bin/waves-server +0 -3
  193. data/verify/foundations/default_application/configurations/development.rb +0 -26
  194. data/verify/foundations/default_application/configurations/mapping.rb +0 -14
  195. data/verify/foundations/default_application/configurations/production.rb +0 -30
  196. data/verify/foundations/default_application/controllers/default.rb +0 -15
  197. data/verify/foundations/default_application/controllers/loaded.rb +0 -15
  198. data/verify/foundations/default_application/defaultapplication.db +0 -0
  199. data/verify/foundations/default_application/helpers/loaded.rb +0 -10
  200. data/verify/foundations/default_application/lib/application.rb +0 -5
  201. data/verify/foundations/default_application/models/default.rb +0 -13
  202. data/verify/foundations/default_application/models/loaded.rb +0 -13
  203. data/verify/foundations/default_application/schema/migrations/templates/empty.rb.erb +0 -9
  204. data/verify/foundations/default_application/startup.rb +0 -7
  205. data/verify/foundations/default_application/templates/errors/not_found_404.mab +0 -2
  206. data/verify/foundations/default_application/templates/errors/server_error_500.mab +0 -2
  207. data/verify/foundations/default_application/templates/layouts/default.mab +0 -14
  208. data/verify/foundations/default_application/views/default.rb +0 -7
  209. data/verify/foundations/default_application/views/loaded.rb +0 -15
  210. data/verify/foundations/helpers.rb +0 -1
  211. data/verify/foundations/simple.rb +0 -25
  212. data/verify/helpers.rb +0 -76
  213. data/verify/layers/data_mapper/association_verify.rb +0 -87
  214. data/verify/layers/default_errors.rb +0 -29
  215. data/verify/layers/helpers.rb +0 -1
  216. data/verify/layers/migration.rb +0 -33
  217. data/verify/layers/sequel/model.rb +0 -41
  218. data/verify/mapping/always.rb +0 -19
  219. data/verify/mapping/filters.rb +0 -65
  220. data/verify/mapping/handle.rb +0 -24
  221. data/verify/mapping/helpers.rb +0 -7
  222. data/verify/mapping/matches.rb +0 -27
  223. data/verify/mapping/named.rb +0 -29
  224. data/verify/mapping/options.rb +0 -17
  225. data/verify/mapping/path.rb +0 -40
  226. data/verify/mapping/response_proxy.rb +0 -50
  227. data/verify/mapping/threaded.rb +0 -25
  228. data/verify/requests/helpers.rb +0 -16
  229. data/verify/requests/request.rb +0 -73
  230. data/verify/requests/response.rb +0 -59
  231. data/verify/requests/session.rb +0 -54
  232. data/verify/views/helpers.rb +0 -1
  233. data/verify/views/rendering.rb +0 -34
  234. data/verify/views/templates/foo.erb +0 -0
  235. data/verify/views/templates/moo.erb +0 -0
  236. data/verify/views/templates/moo.mab +0 -0
@@ -3,62 +3,23 @@ module Waves
3
3
  # Encapsulates the session associated with a given request. A session has an expiration
4
4
  # and path, which must be provided in a Waves::Configuration. Sensible defaults are defined
5
5
  # in Waves::Configurations::Default
6
+
6
7
  class Session
7
-
8
- # Concoct a (probably) unique session id
9
- def self.generate_session_id
10
- # from Camping ...
11
- chars = [*'A'..'Z'] + [*'0'..'9'] + [*'a'..'z']
12
- (0..48).inject(''){|s,x| s+=chars[ rand(chars.length) ] }
13
- end
14
8
 
15
9
  # Create a new session object using the given request. This is not necessarily the
16
- # same as constructing a new session. The session_id cookie for the request domain
17
- # is used to store a session id. The actual session data will be stored in a directory
18
- # specified by the application's configuration file.
10
+ # same as constructing a new session. See Rack::Sesssion for more info.
19
11
  def initialize( request )
20
- @request = request
21
- @data ||= ( File.exist?( session_path ) ? load_session : {} )
12
+ @data = request.rack_request.env['rack.session']
22
13
  end
23
14
 
24
15
  # Return the session data as a hash
25
- def to_hash
26
- @data.to_hash
27
- end
16
+ def to_hash ; @data ; end
28
17
 
29
- def self.base_path
30
- Waves::Application.instance.config.session[:path]
31
- end
32
-
33
- # Save the session data. You shouldn't typically have to call this directly, since it
34
- # is called by Waves::Response#finish.
35
- def save
36
- if @data && @data.length > 0
37
- File.write( session_path, @data.to_yaml )
38
- @request.response.set_cookie( 'session_id',
39
- :value => session_id, :path => '/',
40
- :expires => Time.now + Waves::Server.config.session[:duration] )
41
- end
42
- end
43
-
44
18
  # Access a given data element of the session using the given key.
45
19
  def [](key) ; @data[key] ; end
46
20
  # Set the given data element of the session using the given key and value.
47
21
  def []=(key,val) ; @data[key] = val ; end
48
22
 
49
- private
50
-
51
- def session_id
52
- @session_id ||= ( @request.cookies['session_id'] || Waves::Session.generate_session_id )
53
- end
54
-
55
- def session_path
56
- Waves::Session.base_path / session_id
57
- end
58
-
59
- def load_session
60
- YAML.load( File.read( session_path ) )
61
- end
62
23
 
63
24
  end
64
25
 
@@ -0,0 +1,86 @@
1
+ require 'drb'
2
+ module Waves
3
+
4
+ # "Workers" are just dedicated processes. Managers, Servers, and Monitors are all
5
+ # examples of Workers. This class just encapsulates the common features across all
6
+ # Workers: daemonization, signal traps, console support, logging, only-ness, etc.
7
+
8
+ class Worker < Runtime
9
+
10
+ def self.run( options )
11
+ @instance ||= new( options )
12
+ Kernel.load( options[:startup] || 'startup.rb' )
13
+ @instance.start
14
+ end
15
+
16
+ # make this the one-and-only
17
+ def self.instance ; @instance ; end
18
+ class << self ; private :new, :allocate ; end
19
+ private :dup, :clone
20
+
21
+ # returns the PID of the new process
22
+ def start
23
+ pid = daemonize if options[ :daemon ]
24
+ return pid if pid
25
+ # from here on in, we're in the daemon
26
+ start_logger ; Waves::Logger.info "#{self.class} starting ..."
27
+ start_debugger if debug? unless Kernel.engine == 'jruby'
28
+ # various ways to talk to a worker
29
+ set_traps ; start_console ; start_drb
30
+ start_tasks.join
31
+ end
32
+
33
+ def stop
34
+ Waves::Logger.info "#{self.class} shutting down ..."
35
+ @console.stop if @console
36
+ stop_tasks
37
+ end
38
+
39
+ def restart ; stop ; start ; end
40
+
41
+ def daemonize
42
+ pwd = Dir.pwd ; pid = fork ; return pid if pid ; Dir.chdir( pwd )
43
+ File.umask 0000 ; STDIN.reopen( '/dev/null') ;
44
+ STDOUT.reopen( '/dev/null', 'a' ) ; STDERR.reopen( STDOUT )
45
+ nil # return nil for child process, just like fork does
46
+ end
47
+
48
+ def set_traps
49
+ safe_trap( 'HUP' ) { restart }
50
+ safe_trap( 'TERM','INT' ) { stop }
51
+ end
52
+
53
+ def start_logger
54
+ Waves::Logger.start
55
+ Waves::Logger.info "Logger started."
56
+ end
57
+
58
+ def start_console
59
+ if config.console
60
+ @console = config.console ; @console.start
61
+ Waves::Logger.info "Console started on port #{config.console.port}"
62
+ end
63
+ end
64
+
65
+ def start_debugger
66
+ require 'ruby-debug' ; Debugger.start
67
+ Debugger.settings[:autoeval] = true if Debugger.respond_to?(:settings)
68
+ Waves::Logger.info "ruby-debug enabled"
69
+ end
70
+
71
+ protected
72
+
73
+ # workers should override these methods
74
+ def start_tasks
75
+ end
76
+
77
+ def stop_tasks
78
+ end
79
+
80
+ # for management, monitoring
81
+ def start_drb
82
+ end
83
+
84
+ end
85
+
86
+ end
@@ -0,0 +1,42 @@
1
+ module Waves
2
+
3
+ module Servers
4
+
5
+ # Inherit from this class and define the #call method to create Servers.
6
+ # Like Rack Handlers, except with an attempt at a more generic interface.
7
+ # The #call method should yield with the actual server object.
8
+
9
+ class Base
10
+
11
+ attr_reader :application, :host, :port
12
+ def initialize( application, host, port )
13
+ @application = application
14
+ @host = host ;@port = port
15
+ end
16
+
17
+ # starts server, retrying every second until it succeeds
18
+ def start
19
+ Thread.new do
20
+ connect = false
21
+ until connect do
22
+ begin
23
+ call do |server|
24
+ @server = server
25
+ Waves::Logger.info "#{self.class.basename} started on #{host}:#{port}."
26
+ end
27
+ rescue RuntimeError => e
28
+ Waves::Logger.error e.to_s
29
+ sleep 1
30
+ end
31
+ connect = true
32
+ end
33
+ end
34
+ end
35
+
36
+ def stop
37
+ Waves::Logger.info "#{self.class.basename} on #{host}:#{port} stopped."
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,13 @@
1
+ module Waves
2
+ module Servers
3
+ class Mongrel < Base
4
+
5
+ def call
6
+ Rack::Handler::Mongrel.run( application, :Host => host, :Port => port ) { |server| yield server if block_given? }
7
+ end
8
+
9
+ def stop ; @server.stop ; super ; end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Waves
2
+ module Servers
3
+ class WEBrick < Base
4
+
5
+ def call
6
+ Rack::Handler::WEBrick.run( application, :Host => host, :Port => port ) { | server | yield server if block_given? }
7
+ end
8
+
9
+ def stop ; @server.stop ; super ; end
10
+
11
+ end
12
+ end
13
+ end
data/lib/tasks/gem.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  namespace :gem do
2
+
2
3
  desc "freeze a gem using gem=<gem name> [version=<gem version>]"
3
4
  task :freeze do
4
5
  raise "No gem specified" unless gem_name = ENV['gem']
@@ -1,80 +1,85 @@
1
1
  namespace :generate do
2
-
3
- desc 'Generate a new controller, with name=<name>'
4
- task :controller do |task|
5
- name = ENV['name']
6
- controller_name = name.camel_case
7
- raise "Cannot generate Default yet" if controller_name == 'Default'
8
-
9
- filename = File.expand_path "controllers/#{name}.rb"
10
- if File.exist?(filename)
11
- $stderr.puts "#{filename} already exists"
12
- exit
2
+
3
+ # We're declaring these tasks first so we can add descriptions.
4
+ # The real work is done in the rule, below.
5
+ desc 'Generate new controller, with name=<name>'
6
+ task :controller
7
+
8
+ desc 'Generate new view, with name=<name>'
9
+ task :view
10
+
11
+ desc 'Generate new resource, with name=<name>'
12
+ task :resource
13
+
14
+ # Rake rules are awesome. In the main block, t.name is the task name that matched
15
+ # the regex. t.source is the string returned by the lambda argument to rule.
16
+ rule( /controller|view|resource/ => lambda { |task| basetask(task).camel_case << "s" } ) do |t|
17
+ content = class_template( app_name, t.source, constant_name )
18
+ name = basetask(t.name) << "s"
19
+ File.write( filename( name ), content )
20
+ end
21
+
22
+ desc 'Generate new helper, with name=<name>'
23
+ task :helper do |task|
24
+ content = module_template( app_name, "Helpers", constant_name) do
25
+ "include Waves::Helpers::Default"
13
26
  end
27
+ File.write( filename( "helpers" ), content )
28
+ end
29
+
30
+ end
14
31
 
15
- controller = <<TEXT
16
- module #{Waves.application.name}
17
- module Controllers
18
- class #{controller_name} < Default
32
+ desc "Generate resource, controller, view, and helper with name=<name>"
33
+ task :generate => %w{ generate:resource generate:controller generate:view generate:helper }
19
34
 
20
- end
21
- end
35
+ # Helper methods
36
+
37
+ def app_name
38
+ ( ENV['app'] || Dir.pwd.split('/').last ).camel_case
22
39
  end
23
- TEXT
24
40
 
25
- File.write( filename, controller )
41
+ def constant_name
42
+ str = ENV['name'].camel_case
43
+ raise "Cannot generate Default yet" if str == 'Default'
44
+ str
45
+ end
46
+
47
+ def filename( kind )
48
+ path = File.expand_path "#{kind}/#{ENV['name'].snake_case}.rb"
49
+ if File.exist?(path)
50
+ $stderr.puts " Problem encountered:\n #{path} already exists"
51
+ exit
26
52
  end
27
-
28
- desc 'Generate new view, with name=<name>'
29
- task :view do |task|
30
- name = ENV['name']
31
- view_name = name.camel_case
32
- raise "Cannot generate Default yet" if view_name == 'Default'
33
-
34
- filename = File.expand_path "views/#{name}.rb"
35
- if File.exist?(filename)
36
- $stderr.puts "#{filename} already exists"
37
- exit
38
- end
53
+ path
54
+ end
55
+
56
+ # Rake only pretends to namespace tasks, so to get what we think of as
57
+ # the task name, you must split and grab.
58
+ def basetask(str)
59
+ str.split(":").last
60
+ end
39
61
 
40
- view = <<TEXT
41
- module #{Waves.application.name}
42
- module Views
43
- class #{view_name} < Default
62
+ def class_template(app_name, place, class_name)
63
+ str = <<TEXT
64
+ module #{app_name}
65
+ module #{place}
66
+ class #{class_name} < Default
44
67
 
45
68
  end
46
69
  end
47
70
  end
48
71
  TEXT
72
+ end
49
73
 
50
- File.write( filename, view )
51
- end
52
-
53
- desc 'Generate a new helper, with name=<name>'
54
- task :helper do |task|
55
- name = ENV['name']
56
- helper_name = name.camel_case
57
- raise "Cannot generate Default yet" if helper_name == 'Default'
58
-
59
- filename = File.expand_path "helpers/#{name}.rb"
60
- if File.exist?(filename)
61
- $stderr.puts "#{filename} already exists"
62
- exit
63
- end
64
-
65
- helper = <<TEXT
66
- module #{Waves.application.name}
67
- module Helpers
68
- module #{helper_name}
69
- include Waves::Helpers::Default
70
-
74
+ # This method expects its block to return something usable as a string.
75
+ def module_template(app_name, place, module_name, &block)
76
+ str = <<TEXT
77
+ module #{app_name}
78
+ module #{place}
79
+ module #{module_name}
80
+ #{block.call if block}
71
81
  end
72
82
  end
73
83
  end
74
84
  TEXT
75
-
76
- File.write( filename, helper )
77
- end
78
-
79
-
80
- end
85
+ end
@@ -0,0 +1,49 @@
1
+ require 'helpers/doc_type'
2
+ module Waves
3
+ module Views
4
+ class Errors < Waves::Views::Base
5
+
6
+ include Waves::Helpers::DocType
7
+
8
+ def header( title )
9
+ <<-HTML
10
+ <head>
11
+ <title>#{title}</title>
12
+ <style>
13
+ body { background: #933; padding: 20px; font-family: verdana, sans-serif; }
14
+ h1 { font-size: 60px; font-weight: bold; }
15
+ p { font-size: 24px; }
16
+ </style>
17
+ </head>
18
+ HTML
19
+ end
20
+
21
+ def not_found_404
22
+ DOCTYPES[ :html4_transitional ]
23
+ <<-HTML
24
+ <html>
25
+ #{ header( '404: Not Found' ) }
26
+ <body>
27
+ <h1>404</h1>
28
+ <p>That URL does not exist on this server.</p>
29
+ </body>
30
+ </html>
31
+ HTML
32
+ end
33
+
34
+ def server_error_500
35
+ DOCTYPES[ :html4_transitional ]
36
+ <<-HTML
37
+ <html>
38
+ #{ header( '500: Server Error' ) }
39
+ <body>
40
+ <h1>404</h1>
41
+ <p>Internal server error. Sorry, but your request could not be processed.</p>
42
+ </body>
43
+ </html>
44
+ HTML
45
+ end
46
+
47
+ end
48
+ end
49
+ end
data/lib/views/mixin.rb CHANGED
@@ -1,110 +1,62 @@
1
1
  module Waves
2
2
 
3
- # Views in Waves are ultimately responsible for generating the response body. Views mirror controllers - both have full access to the request and response, and both may modify the response and even short-circuit the request processing and return a result by calling redirect or calling Response#finish.
4
- #
5
- # Views, like controllers, are classes with methods that are invoked by a mapping block (see Waves::Mapping). View instance methods take an assigns hash that are typically converted into instance variables accesible from within a template.
6
- #
7
- # Like controllers, a default implementation is provided by the +waves+ command when you first create your application. This default can be overridden to change the behaviors for all views, or you can explicitly define a View class to provide specific behavior for one view.
8
- #
9
- # The default implementation simply determines which template to render and renders it, returning the result as a string. This machinery is provided by the View mixin, so it is easy to create your own View implementations.
10
- #
11
- # = Templates
12
- #
13
- # Although you won't typically need to modify or define View classes, you will often create and modify templates. Templates can be evaluated using any registered Renderer. Two are presently packaged with Waves: Markaby and Erubis. Templates have access to the assigns hash passed to the view method as instance variables. These can be explicitly defined by the mapping file or whomever is invoking the view.
14
- #
15
- # *Example*
16
- #
17
- # # Find the story named 'home' and pass it as @story into the "story/show" template
18
- # use( :story ) | controller { find( 'home' ) } | view { |x| show( :story => x ) }
19
- #
20
- # = Helpers
21
- #
22
- # Helper methods can be defined for any view template by simply defining them within the default Helper module in <tt>helpers/default.rb</tt> of the generated application. Helpers specific to a particular View class can be explicitly defined by creating a helper module that corresponds to the View class. For examples, for the +User+ View class, you would define a helper module in <tt>user.rb</tt> named +User+.
23
- #
24
- # The default helper class initially includes a wide-variety of helpers, including helpers for layouts, Textile formatting, rendering forms, and nested views, as well as helpers for accessing the request and response objects. More helpers will be added in future releases, but in many cases, there is no need to include all of them in your application.
25
- #
26
- # = Layouts
27
- #
28
- # Layouts are defined using the +Layout+ view class, and layout templates are placed in the +layout+ directory of the +templates+ directory. Layouts are explicitly set within a template using the +layout+ method.
29
- #
30
- # *Example*
31
- #
32
- # layout :default, :title => @story.title do
33
- # h1 @story.title
34
- # textile @story.content
35
- # end
36
- #
37
- #
38
- # The layout method takes a name and an assigns hash that will be available within the layout template as instance variables. In this example, <tt>@title</tt> will be defined as <tt>@story.title</tt> within the layout template named 'default.'
39
- #
40
- # Any number of layouts may be included within a single view, and layouts may even be nested within layouts. This makes it possible to create large numbers of highly structured views that can be easily changed with minimal effort. For example, you might specify a layout associated with form elements. By incorporating this into your views as a +layout+ template, you can make changes across all your forms by changing this single template.
41
- #
42
- # = Nested Views
43
- #
44
- # It is easy to include one view inside another. A common use for this is to define one set of templates for reusable content 'fragments' (somewhat akin to partials in Rails) and another set that incorporate these fragments into specific layouts. Including a view from within another view is done, logically enough, using the +view+ method.
45
- #
46
- # *Example*
47
- #
48
- # # include the summary view for Story
49
- # view :story, :summary, :story => @story
50
- #
51
- # = Request And Response Objects
52
- #
53
- # As always, the request and response objects, and a wide-variety of short-cut methods, are available within view templates via the Waves::ResponseMixin.
54
- #
55
-
56
3
  module Views
57
4
 
58
- class NoTemplateError < Exception ; end
59
-
60
- # A class method that returns the known Renderers, which is any module that is defined within Waves::Renderers and includes the Renderers::Mixin. You can define new Renderers simply be reopening Waves::Renderers and defining a module that mixes in Renderers::Mixin.
61
- def Views.renderers
62
- return [] if Renderers.constants.nil?
63
- Renderers.constants.sort.inject([]) do |rx,cname|
64
- ( Module === (c=Renderers.const_get(cname)) &&
65
- c < Renderers::Mixin ) ? ( rx << c ) : rx
5
+ # A class method that returns the known Renderers, which is any module that is defined within Waves::Renderers and includes the Renderers::Mixin.
6
+ # You can define new Renderers simply be reopening Waves::Renderers and defining a module that mixes in Renderers::Mixin.
7
+ def self.renderers ; @renderers ||= [] ; end
8
+
9
+ def self.renderer_for(path)
10
+ @renderers.find do |renderer|
11
+ File.extname( path ) == ".#{renderer::Extension}" or File.exists?( renderer.filename( path ) )
66
12
  end
67
13
  end
14
+
15
+ def self.render( path, assigns = {} )
16
+ template = Views.renderer_for(path)
17
+ raise NoTemplateError.new( path ) if template.nil?
18
+ template.render( path, assigns )
19
+ end
68
20
 
69
- # The View mixin simply sets up the machinery for invoking a template, along with methods for accessing the request context and the standard interface for invoking a view method.
21
+ class NoTemplateError < Exception # :nodoc:
22
+ end
23
+
24
+ # The View mixin simply sets up the machinery for invoking a template, along with methods for accessing
25
+ # the request assigns and the standard interface for invoking a view method.
70
26
  module Mixin
71
27
 
72
28
  attr_reader :request
73
29
 
74
30
  include Waves::ResponseMixin
75
31
 
76
- def self.included( c )
77
- def c.process( request, *args, &block )
32
+ def self.included( target )
33
+ def target.process( request, *args, &block )
78
34
  self.new( request ).instance_exec( *args, &block )
79
35
  end
80
36
  end
81
37
 
82
- def initialize( request )
83
- @request = request
84
- @layout = :default
85
- end
38
+ def initialize( request ) ; @request = request ; @layout = :default ; end
86
39
 
87
40
  # Return the first renderer for which a template file can be found.
88
41
  # Uses Renderers::Mixin.filename to construct the filename for each renderer.
89
- def renderer(path)
90
- Views.renderers.find do |renderer|
91
- File.exists?( renderer.filename( path ) )
92
- end
42
+ def renderer(path) ; Views.renderer_for( :templates / path) ; end
43
+
44
+ # Render the template found in the directory named after this view (snake cased, of course)
45
+ # E.g. App::Views::Gnome.new.render( "stink" ) would look for templates/gnome/stink.<ext>
46
+ def render( path, assigns = {} )
47
+ qpath = "#{self.class.basename.snake_case}/#{path}"
48
+ Waves.log.debug "Rendering template: #{qpath}"
49
+ Views.render( :templates / qpath, assigns.merge!( :request => request ))
93
50
  end
94
51
 
95
- def render( path, context = {} )
96
- context.merge!( :request => request )
97
- template = renderer( path ) || renderer( :generic / File.basename(path) )
98
- raise NoTemplateError.new( path ) if template.nil?
99
- template.render( path, context )
100
- end
101
-
102
- def method_missing(name,*args)
103
- render( "/#{self.class.basename.snake_case}/#{name}", *args )
104
- end
52
+ # Render the template with the name of the missing method.
53
+ def method_missing(name,*args) ; render( name, *args ) ; end
105
54
 
106
55
  end
107
56
 
57
+ class Base ; include Mixin ; end
58
+
108
59
  end
60
+
109
61
 
110
62
  end