raw 0.49.0

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 (148) hide show
  1. data/doc/CONTRIBUTORS +106 -0
  2. data/doc/LICENSE +32 -0
  3. data/doc/coding_conventions.txt +11 -0
  4. data/lib/raw.rb +42 -0
  5. data/lib/raw/adapter.rb +113 -0
  6. data/lib/raw/adapter/cgi.rb +41 -0
  7. data/lib/raw/adapter/fastcgi.rb +48 -0
  8. data/lib/raw/adapter/mongrel.rb +146 -0
  9. data/lib/raw/adapter/script.rb +94 -0
  10. data/lib/raw/adapter/webrick.rb +144 -0
  11. data/lib/raw/adapter/webrick/vcr.rb +91 -0
  12. data/lib/raw/cgi.rb +323 -0
  13. data/lib/raw/cgi/cookie.rb +47 -0
  14. data/lib/raw/cgi/http.rb +62 -0
  15. data/lib/raw/compiler.rb +138 -0
  16. data/lib/raw/compiler/filter/cleanup.rb +21 -0
  17. data/lib/raw/compiler/filter/elements.rb +166 -0
  18. data/lib/raw/compiler/filter/elements/element.rb +210 -0
  19. data/lib/raw/compiler/filter/localization.rb +23 -0
  20. data/lib/raw/compiler/filter/markup.rb +32 -0
  21. data/lib/raw/compiler/filter/morph.rb +123 -0
  22. data/lib/raw/compiler/filter/morph/each.rb +34 -0
  23. data/lib/raw/compiler/filter/morph/for.rb +11 -0
  24. data/lib/raw/compiler/filter/morph/if.rb +26 -0
  25. data/lib/raw/compiler/filter/morph/selected_if.rb +43 -0
  26. data/lib/raw/compiler/filter/morph/standard.rb +55 -0
  27. data/lib/raw/compiler/filter/morph/times.rb +27 -0
  28. data/lib/raw/compiler/filter/script.rb +116 -0
  29. data/lib/raw/compiler/filter/squeeze.rb +16 -0
  30. data/lib/raw/compiler/filter/static_include.rb +74 -0
  31. data/lib/raw/compiler/filter/template.rb +121 -0
  32. data/lib/raw/compiler/reloader.rb +96 -0
  33. data/lib/raw/context.rb +154 -0
  34. data/lib/raw/context/flash.rb +157 -0
  35. data/lib/raw/context/global.rb +88 -0
  36. data/lib/raw/context/request.rb +338 -0
  37. data/lib/raw/context/response.rb +57 -0
  38. data/lib/raw/context/session.rb +198 -0
  39. data/lib/raw/context/session/drb.rb +11 -0
  40. data/lib/raw/context/session/file.rb +15 -0
  41. data/lib/raw/context/session/memcached.rb +13 -0
  42. data/lib/raw/context/session/memory.rb +12 -0
  43. data/lib/raw/context/session/og.rb +15 -0
  44. data/lib/raw/context/session/pstore.rb +13 -0
  45. data/lib/raw/control.rb +18 -0
  46. data/lib/raw/control/attribute.rb +91 -0
  47. data/lib/raw/control/attribute/checkbox.rb +25 -0
  48. data/lib/raw/control/attribute/datetime.rb +21 -0
  49. data/lib/raw/control/attribute/file.rb +20 -0
  50. data/lib/raw/control/attribute/fixnum.rb +26 -0
  51. data/lib/raw/control/attribute/float.rb +26 -0
  52. data/lib/raw/control/attribute/options.rb +38 -0
  53. data/lib/raw/control/attribute/password.rb +16 -0
  54. data/lib/raw/control/attribute/text.rb +16 -0
  55. data/lib/raw/control/attribute/textarea.rb +16 -0
  56. data/lib/raw/control/none.rb +16 -0
  57. data/lib/raw/control/relation.rb +59 -0
  58. data/lib/raw/control/relation/belongs_to.rb +0 -0
  59. data/lib/raw/control/relation/has_many.rb +97 -0
  60. data/lib/raw/control/relation/joins_many.rb +0 -0
  61. data/lib/raw/control/relation/many_to_many.rb +0 -0
  62. data/lib/raw/control/relation/refers_to.rb +29 -0
  63. data/lib/raw/controller.rb +37 -0
  64. data/lib/raw/controller/publishable.rb +160 -0
  65. data/lib/raw/dispatcher.rb +209 -0
  66. data/lib/raw/dispatcher/format.rb +108 -0
  67. data/lib/raw/dispatcher/format/atom.rb +31 -0
  68. data/lib/raw/dispatcher/format/css.rb +0 -0
  69. data/lib/raw/dispatcher/format/html.rb +42 -0
  70. data/lib/raw/dispatcher/format/json.rb +31 -0
  71. data/lib/raw/dispatcher/format/rss.rb +33 -0
  72. data/lib/raw/dispatcher/format/xoxo.rb +31 -0
  73. data/lib/raw/dispatcher/mounter.rb +60 -0
  74. data/lib/raw/dispatcher/router.rb +111 -0
  75. data/lib/raw/errors.rb +19 -0
  76. data/lib/raw/helper.rb +86 -0
  77. data/lib/raw/helper/benchmark.rb +23 -0
  78. data/lib/raw/helper/buffer.rb +60 -0
  79. data/lib/raw/helper/cookie.rb +32 -0
  80. data/lib/raw/helper/debug.rb +28 -0
  81. data/lib/raw/helper/default.rb +16 -0
  82. data/lib/raw/helper/feed.rb +451 -0
  83. data/lib/raw/helper/form.rb +284 -0
  84. data/lib/raw/helper/javascript.rb +59 -0
  85. data/lib/raw/helper/layout.rb +40 -0
  86. data/lib/raw/helper/navigation.rb +87 -0
  87. data/lib/raw/helper/pager.rb +305 -0
  88. data/lib/raw/helper/table.rb +247 -0
  89. data/lib/raw/helper/xhtml.rb +218 -0
  90. data/lib/raw/helper/xml.rb +125 -0
  91. data/lib/raw/mixin/magick.rb +35 -0
  92. data/lib/raw/mixin/sweeper.rb +71 -0
  93. data/lib/raw/mixin/thumbnails.rb +1 -0
  94. data/lib/raw/mixin/webfile.rb +165 -0
  95. data/lib/raw/render.rb +271 -0
  96. data/lib/raw/render/builder.rb +26 -0
  97. data/lib/raw/render/caching.rb +81 -0
  98. data/lib/raw/render/call.rb +43 -0
  99. data/lib/raw/render/send_file.rb +46 -0
  100. data/lib/raw/render/stream.rb +39 -0
  101. data/lib/raw/scaffold.rb +13 -0
  102. data/lib/raw/scaffold/controller.rb +25 -0
  103. data/lib/raw/scaffold/model.rb +157 -0
  104. data/lib/raw/test.rb +5 -0
  105. data/lib/raw/test/assertions.rb +169 -0
  106. data/lib/raw/test/context.rb +55 -0
  107. data/lib/raw/test/testcase.rb +79 -0
  108. data/lib/raw/util/attr.rb +128 -0
  109. data/lib/raw/util/encode_uri.rb +149 -0
  110. data/lib/raw/util/html_filter.rb +538 -0
  111. data/lib/raw/util/markup.rb +130 -0
  112. data/test/glue/tc_webfile.rb +1 -0
  113. data/test/nitro/CONFIG.rb +3 -0
  114. data/test/nitro/adapter/raw_post1.bin +9 -0
  115. data/test/nitro/adapter/tc_webrick.rb +16 -0
  116. data/test/nitro/cgi/tc_cookie.rb +14 -0
  117. data/test/nitro/cgi/tc_request.rb +61 -0
  118. data/test/nitro/compiler/tc_client_morpher.rb +47 -0
  119. data/test/nitro/compiler/tc_compiler.rb +25 -0
  120. data/test/nitro/dispatcher/tc_mounter.rb +47 -0
  121. data/test/nitro/helper/tc_feed.rb +135 -0
  122. data/test/nitro/helper/tc_navbar.rb +74 -0
  123. data/test/nitro/helper/tc_pager.rb +35 -0
  124. data/test/nitro/helper/tc_table.rb +68 -0
  125. data/test/nitro/helper/tc_xhtml.rb +19 -0
  126. data/test/nitro/tc_caching.rb +19 -0
  127. data/test/nitro/tc_cgi.rb +222 -0
  128. data/test/nitro/tc_context.rb +17 -0
  129. data/test/nitro/tc_controller.rb +103 -0
  130. data/test/nitro/tc_controller_aspect.rb +32 -0
  131. data/test/nitro/tc_controller_params.rb +885 -0
  132. data/test/nitro/tc_dispatcher.rb +109 -0
  133. data/test/nitro/tc_element.rb +85 -0
  134. data/test/nitro/tc_flash.rb +59 -0
  135. data/test/nitro/tc_helper.rb +47 -0
  136. data/test/nitro/tc_render.rb +119 -0
  137. data/test/nitro/tc_router.rb +61 -0
  138. data/test/nitro/tc_server.rb +35 -0
  139. data/test/nitro/tc_session.rb +66 -0
  140. data/test/nitro/tc_template.rb +71 -0
  141. data/test/nitro/util/tc_encode_url.rb +87 -0
  142. data/test/nitro/util/tc_markup.rb +31 -0
  143. data/test/public/blog/another/very_litle/index.xhtml +1 -0
  144. data/test/public/blog/inc1.xhtml +2 -0
  145. data/test/public/blog/inc2.xhtml +1 -0
  146. data/test/public/blog/list.xhtml +9 -0
  147. data/test/public/dummy_mailer/registration.xhtml +5 -0
  148. metadata +244 -0
@@ -0,0 +1,160 @@
1
+ require "facets/more/ann"
2
+
3
+ require "raw/render"
4
+ require "raw/render/caching"
5
+ require "raw/context/flash"
6
+ require "raw/helper"
7
+ require "raw/helper/cookie"
8
+ require "raw/compiler"
9
+ require "raw/compiler/filter/template"
10
+ require "raw/util/encode_uri"
11
+ require "raw/util/markup"
12
+
13
+ module Raw
14
+
15
+ # Include this Mixin to a class to make objects of this class
16
+ # publishable, ie accessible through a standard web (REST)
17
+ # interface.
18
+
19
+ module Publishable
20
+ def self.included(base)
21
+ super
22
+ base.send(:include, Render)
23
+ base.send(:include, Flashing)
24
+ base.send(:include, Caching)
25
+ base.send(:include, Helpers)
26
+ base.helper(EncodeURI)
27
+ base.helper(CookieHelper)
28
+ base.helper(Markup)
29
+ end
30
+
31
+ # Use the method_missing hook to compile the actions
32
+ # for this controller.
33
+
34
+ def method_missing(action, *args)
35
+ if Context.current.application.compiler.compile(self.class, action)
36
+ send(action, *args)
37
+ else
38
+ super
39
+ end
40
+ end
41
+
42
+ class_extension do
43
+
44
+ # The path where this controller is mounted.
45
+
46
+ attr_accessor :mount_path
47
+
48
+ # Return the 'action' methods for this Object. Some
49
+ # dangerous methods from ancestors are removed. All private
50
+ # methods are ignored.
51
+ #--
52
+ # gmosx, TODO: We should optimize this method.
53
+ #++
54
+
55
+ def action_methods
56
+ public_instance_methods - Controller.public_instance_methods
57
+ end
58
+ alias_method :actions, :action_methods
59
+
60
+ # Check if the controller responds to this action.
61
+
62
+ def action?(action)
63
+ action_methods.include?(action.to_s)
64
+ end
65
+ alias_method :respond_to_action?, :action?
66
+
67
+ # Check if the a template for this action and format exists.
68
+ # Returns a valid path or nil.
69
+
70
+ def template?(action, format)
71
+ # Allow for template override using the :template annotation
72
+ #
73
+ # class MyController
74
+ # def myaction
75
+ # end
76
+ # ann :myaction, :template => :another_template
77
+ # end
78
+
79
+ template = ann(action, :template) || action
80
+
81
+ template = template.to_s.gsub(/__/, "/")
82
+
83
+ for dir in ann(:self, :template_dir_stack)
84
+ name = "#{dir}/#{template}".squeeze("/")
85
+
86
+ # attempt to find a template of the form:
87
+ # dir/action.xhtml
88
+
89
+ path = "#{name}.#{format.template_extension}"
90
+ return path if File.exist?(path)
91
+
92
+ # attempt to find a template of the form:
93
+ # dir/action/index.xhtml
94
+
95
+ path = "#{name}/index.#{format.template_extension}"
96
+ return path if File.exist?(path)
97
+ end
98
+
99
+ return nil
100
+ end
101
+ alias_method :has_template?, :template?
102
+ alias_method :template_path, :template?
103
+
104
+ # Check if this action or template exists.
105
+
106
+ def action_or_template?(action, format)
107
+ action?(action) or template?(action, format)
108
+ end
109
+ alias_method :respond_to_action_or_template?, :action_or_template?
110
+
111
+ # Aliases an action
112
+ #--
113
+ # gmosx, FIXME: better implementation needed.
114
+ # gmosx, FIXME: copy all annotations.
115
+ #++
116
+
117
+ def alias_action(new, old)
118
+ alias_method new, old
119
+ ann new, :template => old
120
+ end
121
+
122
+ # Override this method to customize the template_dir_stack.
123
+ # Typically used in controllers defined in reusable Parts.
124
+ # Call super to include the parent class's customizations.
125
+ # Implements some form of template root inheritance,
126
+ # thus allowing for more reusable controllers. Ie you can
127
+ # 'extend' a controller, and only override the templates
128
+ # you want to change. The compiler will traverse the
129
+ # template dir stack and use the templates from parent
130
+ # controllers if they are not overriden.
131
+ #
132
+ # def self.setup_template_dir_stack(stack)
133
+ # super
134
+ # stack << "custom/route/#{self.mount_path}"
135
+ # stack << "another/route"
136
+ # end
137
+
138
+ def setup_template_dir_stack(path)
139
+ end
140
+
141
+ # This callback is called when this controller is mounted.
142
+
143
+ def mount_at(path)
144
+ @mount_path = path
145
+
146
+ # Setup the template_dir_stack.
147
+
148
+ stack = []
149
+ stack << File.join(Template.root_dir, path).gsub(/\/$/, "")
150
+ self.setup_template_dir_stack(stack)
151
+ stack << File.join(Nitro.proto_path, "template", path).gsub(/\/$/, "")
152
+ ann(:self, :template_dir_stack => stack)
153
+ end
154
+ alias_method :mount, :mount_at
155
+
156
+ end # class_extension
157
+
158
+ end
159
+
160
+ end
@@ -0,0 +1,209 @@
1
+ require "facets/core/module/ancestor"
2
+
3
+ require "raw/dispatcher/router"
4
+ require "raw/dispatcher/mounter"
5
+ require "raw/dispatcher/format"
6
+
7
+ module Raw
8
+
9
+ # The Dispatcher manages a set of controllers. It selects the
10
+ # appropriate Controller and action to handle the given
11
+ # request.
12
+ #
13
+ # This dispatcher intelligently handles RESTful uris according
14
+ # to the following scheme:
15
+ #
16
+ # GET /links GET /links/index Link::Controller#index
17
+ # POST /links POST /links/create Link::Controller#create
18
+ # GET /links;new GET /links/new Link::Controller#new
19
+ # GET /links/1 Link::Controller#view(1)
20
+ # GET /links/1;edit GET /links/edit/1 Link::Controller#edit(1)
21
+ # PUT /links/1 POST /links/update/1 Link::Controller#update
22
+ # DELETE /links/1 GET /links/delete/1 Link::Controller#delete(1)
23
+ # GET /links/index.xml Link::Controller#index # Atom
24
+ # GET /links/index.json Link::Controller#index # JSON
25
+ #
26
+ # The default actions for the various methods are:
27
+ #
28
+ # GET: index
29
+ # POST: create
30
+ # PUT: update
31
+ # DELETE: delete
32
+
33
+ class Dispatcher
34
+
35
+ # The (optional) router.
36
+
37
+ attr_accessor :router
38
+
39
+ # The hash that maps mount paths to controllers.
40
+
41
+ attr_accessor :controllers
42
+
43
+ # The representation formats this dispatcher understands.
44
+
45
+ attr_accessor :formats
46
+
47
+ # Initialize the dispatcher.
48
+
49
+ def initialize(controller_or_map = nil)
50
+ @controllers = {}
51
+ @formats = Nitro::STANDARD_FORMATS.dup
52
+
53
+ if controller_or_map.is_a?(Class)
54
+ mount("/" => controller_or_map)
55
+ elsif controller_or_map.is_a?(Hash)
56
+ mount(controller_or_map)
57
+ end
58
+ end
59
+
60
+ # Mounts a map of controllers.
61
+
62
+ def mount(map)
63
+ for path, controller in map
64
+ self[path] = controller
65
+ end
66
+ end
67
+
68
+ # Return the controller for the given mount path.
69
+
70
+ def [](path = "/")
71
+ @controllers[path]
72
+ end
73
+
74
+ # Mount a controller to the given mount path.
75
+
76
+ def []=(path, controller)
77
+ controller = resolve_controller(controller)
78
+
79
+ # Customize the class for mounting at the given path.
80
+ controller.mount_at(path) if controller.respond_to? :mount_at
81
+
82
+ # Call the mounted callback to allow for post mount
83
+ # initialization.
84
+ controller.mounted(path) if controller.respond_to? :mounted
85
+
86
+ @controllers[path] = controller
87
+ end
88
+
89
+ # Dispatch a request. Calls the lower level dispatch method.
90
+
91
+ def dispatch_request(request)
92
+ dispatch(request.uri, request.method)
93
+ end
94
+ alias_method :dispatch_context, :dispatch_request
95
+
96
+ # Dispatch a path given the request method. This method
97
+ # handles fully resolved paths (containing an extension that
98
+ # denotes the expected content type).
99
+ #
100
+ # This method automatically handles 'nice' (seo friendly, elegant)
101
+ # parameters, ie:
102
+ #
103
+ # /links/view/1
104
+ #
105
+ # instead of
106
+ #
107
+ # /links/view?oid=1
108
+ #
109
+ # === Output
110
+ #
111
+ # controller, action, query_string, nice_params, extension
112
+ #
113
+ #--
114
+ # Lower level, useful for testing.
115
+ #++
116
+
117
+ def dispatch(uri, method = :get)
118
+ # Extract the query string.
119
+
120
+ path, query = uri.split("?", 2)
121
+
122
+ # Try to route the path.
123
+
124
+ path = @router.route(path) if @router
125
+
126
+ # The characters after the last '.' in the path form the
127
+ # extension that itself represents the expected response
128
+ # content type.
129
+
130
+ ext = File.extname(path)[1..-1] || "html"
131
+
132
+ # The resource representation format for this request.
133
+
134
+ unless format = Context.current.format = @formats.by_extension[ext]
135
+ raise ActionError.new("Cannot respond to '#{path}' using the '#{ext}' format representation.")
136
+ end
137
+
138
+ # Remove the extension from the path.
139
+
140
+ path = path.gsub(/\.(.*)$/, '')
141
+
142
+ # Try to extract the controller from the path (that may also
143
+ # include 'nice' parameters). This algorithm tries to find
144
+ # the bigest substring of the path that represents a mount
145
+ # path for a controller.
146
+
147
+ key = path.dup
148
+
149
+ while (controller = @controllers[key]).nil?
150
+ key = key[%r{^(/.+)/.+$}, 1] || '/'
151
+ end
152
+
153
+ # Try to extract the controller from the path. This
154
+ # algorithm tries to find the bigest substring of the path
155
+ # that represents an action of this controller.
156
+ #
157
+ # The algorithm respects action name conventions, ie
158
+ # simple/sub/action maps to simple__sub__action.
159
+
160
+ action = key = path.sub(%r{^#{key}}, '').gsub(%r{^/}, '').gsub(%r{/}, '__')
161
+
162
+ while (!action.blank?) and !controller.action_or_template?(action, format)
163
+ action = action[%r{^(.+)__.+$}, 1]
164
+ end
165
+
166
+ # Extract the 'nice' parameters.
167
+
168
+ params = key.sub(%r{^#{action}}, '').gsub(/^__/, '').split('__')
169
+
170
+ # Do we have an action?
171
+
172
+ if action.blank?
173
+ # Try to use a standard action for this http method.
174
+
175
+ case method
176
+ when :get
177
+ action = "index"
178
+
179
+ when :post
180
+ action = "create"
181
+
182
+ when :delete
183
+ action = "delete"
184
+
185
+ when :put
186
+ action = "update"
187
+ end
188
+
189
+ unless controller.action_or_template?(action, format)
190
+ raise ActionError.new("Cannot respond to '#{path}' using '#{controller}'")
191
+ end
192
+ end
193
+
194
+ return controller, "#{action}___super", query, params, ext
195
+ end
196
+
197
+ private
198
+
199
+ def resolve_controller(controller)
200
+ unless controller.ancestor?(Controller) or controller.ancestor?(Publishable)
201
+ controller.send(:include, Publishable)
202
+ end
203
+
204
+ return controller
205
+ end
206
+
207
+ end
208
+
209
+ end
@@ -0,0 +1,108 @@
1
+ module Raw
2
+
3
+ # A REST Resource Representation format.
4
+
5
+ class Format
6
+
7
+ # The name of this format.
8
+
9
+ attr_accessor :name
10
+
11
+ # The resource content type. Typically the resource MIME type
12
+ # is used.
13
+
14
+ attr_accessor :content_type
15
+ alias_method :mime_type, :content_type
16
+
17
+ # The default resource extension.
18
+
19
+ attr_accessor :extension
20
+
21
+ # The default template extension.
22
+
23
+ attr_accessor :template_extension
24
+
25
+ def to_s
26
+ @name
27
+ end
28
+
29
+ # Apply filters to the template source. The original template
30
+ # representation must be transformed to executable Ruby code
31
+ # at the end.
32
+
33
+ def filter_template(source)
34
+ return source
35
+ end
36
+
37
+ # This callback is called before the action is executed with
38
+ # this format.
39
+
40
+ def before_action(controller, context)
41
+ end
42
+
43
+ # This callback is called after the action is executed with
44
+ # this format.
45
+
46
+ def after_action(controller, context)
47
+ end
48
+
49
+ end
50
+
51
+ # A Format Manager. Provides useful methods for fast Format
52
+ # lookup.
53
+
54
+ class FormatManager
55
+ # Formats indexed by name.
56
+
57
+ attr_accessor :by_name
58
+
59
+ # Formats indexed by mime type.
60
+
61
+ attr_accessor :by_mime_type
62
+
63
+ # Formats indexed by extension.
64
+
65
+ attr_accessor :by_extension
66
+
67
+ def initialize(*formats)
68
+ @by_name = {}
69
+ @by_mime_type = {}
70
+ @by_extension = {}
71
+
72
+ for format in formats.flatten
73
+ put(format)
74
+ end
75
+ end
76
+
77
+ # Add a new format to the manager.
78
+
79
+ def put(format)
80
+ if format.is_a? Class
81
+ format = format.new
82
+ end
83
+
84
+ @by_name[format.name] = format
85
+ @by_mime_type[format.mime_type] = format
86
+ @by_extension[format.extension] = format
87
+ end
88
+ alias_method :<<, :put
89
+
90
+ # Lookup a format by name.
91
+
92
+ def [](name)
93
+ @by_name[name]
94
+ end
95
+
96
+ end
97
+
98
+ require "raw/dispatcher/format/html"
99
+ require "raw/dispatcher/format/atom"
100
+ require "raw/dispatcher/format/rss"
101
+ require "raw/dispatcher/format/json"
102
+ require "raw/dispatcher/format/xoxo"
103
+
104
+ STANDARD_FORMATS = FormatManager.new(
105
+ HTMLFormat, ATOMFormat, RSSFormat, JSONFormat, XOXOFormat
106
+ )
107
+
108
+ end