raw 0.49.0
Sign up to get free protection for your applications and to get access to all the features.
- data/doc/CONTRIBUTORS +106 -0
- data/doc/LICENSE +32 -0
- data/doc/coding_conventions.txt +11 -0
- data/lib/raw.rb +42 -0
- data/lib/raw/adapter.rb +113 -0
- data/lib/raw/adapter/cgi.rb +41 -0
- data/lib/raw/adapter/fastcgi.rb +48 -0
- data/lib/raw/adapter/mongrel.rb +146 -0
- data/lib/raw/adapter/script.rb +94 -0
- data/lib/raw/adapter/webrick.rb +144 -0
- data/lib/raw/adapter/webrick/vcr.rb +91 -0
- data/lib/raw/cgi.rb +323 -0
- data/lib/raw/cgi/cookie.rb +47 -0
- data/lib/raw/cgi/http.rb +62 -0
- data/lib/raw/compiler.rb +138 -0
- data/lib/raw/compiler/filter/cleanup.rb +21 -0
- data/lib/raw/compiler/filter/elements.rb +166 -0
- data/lib/raw/compiler/filter/elements/element.rb +210 -0
- data/lib/raw/compiler/filter/localization.rb +23 -0
- data/lib/raw/compiler/filter/markup.rb +32 -0
- data/lib/raw/compiler/filter/morph.rb +123 -0
- data/lib/raw/compiler/filter/morph/each.rb +34 -0
- data/lib/raw/compiler/filter/morph/for.rb +11 -0
- data/lib/raw/compiler/filter/morph/if.rb +26 -0
- data/lib/raw/compiler/filter/morph/selected_if.rb +43 -0
- data/lib/raw/compiler/filter/morph/standard.rb +55 -0
- data/lib/raw/compiler/filter/morph/times.rb +27 -0
- data/lib/raw/compiler/filter/script.rb +116 -0
- data/lib/raw/compiler/filter/squeeze.rb +16 -0
- data/lib/raw/compiler/filter/static_include.rb +74 -0
- data/lib/raw/compiler/filter/template.rb +121 -0
- data/lib/raw/compiler/reloader.rb +96 -0
- data/lib/raw/context.rb +154 -0
- data/lib/raw/context/flash.rb +157 -0
- data/lib/raw/context/global.rb +88 -0
- data/lib/raw/context/request.rb +338 -0
- data/lib/raw/context/response.rb +57 -0
- data/lib/raw/context/session.rb +198 -0
- data/lib/raw/context/session/drb.rb +11 -0
- data/lib/raw/context/session/file.rb +15 -0
- data/lib/raw/context/session/memcached.rb +13 -0
- data/lib/raw/context/session/memory.rb +12 -0
- data/lib/raw/context/session/og.rb +15 -0
- data/lib/raw/context/session/pstore.rb +13 -0
- data/lib/raw/control.rb +18 -0
- data/lib/raw/control/attribute.rb +91 -0
- data/lib/raw/control/attribute/checkbox.rb +25 -0
- data/lib/raw/control/attribute/datetime.rb +21 -0
- data/lib/raw/control/attribute/file.rb +20 -0
- data/lib/raw/control/attribute/fixnum.rb +26 -0
- data/lib/raw/control/attribute/float.rb +26 -0
- data/lib/raw/control/attribute/options.rb +38 -0
- data/lib/raw/control/attribute/password.rb +16 -0
- data/lib/raw/control/attribute/text.rb +16 -0
- data/lib/raw/control/attribute/textarea.rb +16 -0
- data/lib/raw/control/none.rb +16 -0
- data/lib/raw/control/relation.rb +59 -0
- data/lib/raw/control/relation/belongs_to.rb +0 -0
- data/lib/raw/control/relation/has_many.rb +97 -0
- data/lib/raw/control/relation/joins_many.rb +0 -0
- data/lib/raw/control/relation/many_to_many.rb +0 -0
- data/lib/raw/control/relation/refers_to.rb +29 -0
- data/lib/raw/controller.rb +37 -0
- data/lib/raw/controller/publishable.rb +160 -0
- data/lib/raw/dispatcher.rb +209 -0
- data/lib/raw/dispatcher/format.rb +108 -0
- data/lib/raw/dispatcher/format/atom.rb +31 -0
- data/lib/raw/dispatcher/format/css.rb +0 -0
- data/lib/raw/dispatcher/format/html.rb +42 -0
- data/lib/raw/dispatcher/format/json.rb +31 -0
- data/lib/raw/dispatcher/format/rss.rb +33 -0
- data/lib/raw/dispatcher/format/xoxo.rb +31 -0
- data/lib/raw/dispatcher/mounter.rb +60 -0
- data/lib/raw/dispatcher/router.rb +111 -0
- data/lib/raw/errors.rb +19 -0
- data/lib/raw/helper.rb +86 -0
- data/lib/raw/helper/benchmark.rb +23 -0
- data/lib/raw/helper/buffer.rb +60 -0
- data/lib/raw/helper/cookie.rb +32 -0
- data/lib/raw/helper/debug.rb +28 -0
- data/lib/raw/helper/default.rb +16 -0
- data/lib/raw/helper/feed.rb +451 -0
- data/lib/raw/helper/form.rb +284 -0
- data/lib/raw/helper/javascript.rb +59 -0
- data/lib/raw/helper/layout.rb +40 -0
- data/lib/raw/helper/navigation.rb +87 -0
- data/lib/raw/helper/pager.rb +305 -0
- data/lib/raw/helper/table.rb +247 -0
- data/lib/raw/helper/xhtml.rb +218 -0
- data/lib/raw/helper/xml.rb +125 -0
- data/lib/raw/mixin/magick.rb +35 -0
- data/lib/raw/mixin/sweeper.rb +71 -0
- data/lib/raw/mixin/thumbnails.rb +1 -0
- data/lib/raw/mixin/webfile.rb +165 -0
- data/lib/raw/render.rb +271 -0
- data/lib/raw/render/builder.rb +26 -0
- data/lib/raw/render/caching.rb +81 -0
- data/lib/raw/render/call.rb +43 -0
- data/lib/raw/render/send_file.rb +46 -0
- data/lib/raw/render/stream.rb +39 -0
- data/lib/raw/scaffold.rb +13 -0
- data/lib/raw/scaffold/controller.rb +25 -0
- data/lib/raw/scaffold/model.rb +157 -0
- data/lib/raw/test.rb +5 -0
- data/lib/raw/test/assertions.rb +169 -0
- data/lib/raw/test/context.rb +55 -0
- data/lib/raw/test/testcase.rb +79 -0
- data/lib/raw/util/attr.rb +128 -0
- data/lib/raw/util/encode_uri.rb +149 -0
- data/lib/raw/util/html_filter.rb +538 -0
- data/lib/raw/util/markup.rb +130 -0
- data/test/glue/tc_webfile.rb +1 -0
- data/test/nitro/CONFIG.rb +3 -0
- data/test/nitro/adapter/raw_post1.bin +9 -0
- data/test/nitro/adapter/tc_webrick.rb +16 -0
- data/test/nitro/cgi/tc_cookie.rb +14 -0
- data/test/nitro/cgi/tc_request.rb +61 -0
- data/test/nitro/compiler/tc_client_morpher.rb +47 -0
- data/test/nitro/compiler/tc_compiler.rb +25 -0
- data/test/nitro/dispatcher/tc_mounter.rb +47 -0
- data/test/nitro/helper/tc_feed.rb +135 -0
- data/test/nitro/helper/tc_navbar.rb +74 -0
- data/test/nitro/helper/tc_pager.rb +35 -0
- data/test/nitro/helper/tc_table.rb +68 -0
- data/test/nitro/helper/tc_xhtml.rb +19 -0
- data/test/nitro/tc_caching.rb +19 -0
- data/test/nitro/tc_cgi.rb +222 -0
- data/test/nitro/tc_context.rb +17 -0
- data/test/nitro/tc_controller.rb +103 -0
- data/test/nitro/tc_controller_aspect.rb +32 -0
- data/test/nitro/tc_controller_params.rb +885 -0
- data/test/nitro/tc_dispatcher.rb +109 -0
- data/test/nitro/tc_element.rb +85 -0
- data/test/nitro/tc_flash.rb +59 -0
- data/test/nitro/tc_helper.rb +47 -0
- data/test/nitro/tc_render.rb +119 -0
- data/test/nitro/tc_router.rb +61 -0
- data/test/nitro/tc_server.rb +35 -0
- data/test/nitro/tc_session.rb +66 -0
- data/test/nitro/tc_template.rb +71 -0
- data/test/nitro/util/tc_encode_url.rb +87 -0
- data/test/nitro/util/tc_markup.rb +31 -0
- data/test/public/blog/another/very_litle/index.xhtml +1 -0
- data/test/public/blog/inc1.xhtml +2 -0
- data/test/public/blog/inc2.xhtml +1 -0
- data/test/public/blog/list.xhtml +9 -0
- data/test/public/dummy_mailer/registration.xhtml +5 -0
- metadata +244 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
module Raw
|
2
|
+
|
3
|
+
# Encapsulates a HTTP Cookie.
|
4
|
+
|
5
|
+
class Cookie
|
6
|
+
attr_reader :name
|
7
|
+
attr_accessor :value, :version
|
8
|
+
attr_accessor :domain, :path, :secure
|
9
|
+
attr_accessor :comment, :max_age
|
10
|
+
|
11
|
+
def initialize(name = nil, value = nil, expires = nil)
|
12
|
+
@name = name
|
13
|
+
@value = value
|
14
|
+
self.expires = expires
|
15
|
+
@version = 0 # Netscape Cookie THINK: maybe should make this 1 ??
|
16
|
+
@path = "/" # gmosx: KEEP this!
|
17
|
+
@domain = @secure = @comment = @max_age = nil
|
18
|
+
@discard = @port = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
# Set the cookie expiration.
|
22
|
+
|
23
|
+
def expires=(t)
|
24
|
+
@expires = t && (t.is_a?(Time) ? t.httpdate : t.to_s)
|
25
|
+
end
|
26
|
+
|
27
|
+
# When the cookie expires.
|
28
|
+
|
29
|
+
def expires
|
30
|
+
@expires && Time.parse(@expires)
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
str = "#{@name}=#{@value}"
|
35
|
+
str << "; Version=#{@version}" if @version > 0
|
36
|
+
str << "; Domain=#{@domain}" if @domain
|
37
|
+
str << "; Expires=#{@expires}" if @expires
|
38
|
+
str << "; Max-Age=#{@max_age}" if @max_age
|
39
|
+
str << "; Comment=#{@comment}" if @comment
|
40
|
+
str << "; Path=#{@path}" if @path
|
41
|
+
str << "; Secure" if @secure
|
42
|
+
return str
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
data/lib/raw/cgi/http.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
module Raw
|
2
|
+
|
3
|
+
# Various HTTP related constants and utilities.
|
4
|
+
|
5
|
+
module Http
|
6
|
+
|
7
|
+
# HTTP protocol EOL constants.
|
8
|
+
|
9
|
+
CR = "\x0d"
|
10
|
+
LF = "\x0a"
|
11
|
+
CRLF = "\x0d\x0a"
|
12
|
+
EOL = CRLF
|
13
|
+
|
14
|
+
# Constants for readable code.
|
15
|
+
|
16
|
+
STATUS_OK = 200
|
17
|
+
STATUS_PARTIAL_CONTENT = 206
|
18
|
+
STATUS_MOVED = 301
|
19
|
+
STATUS_REDIRECT = 302
|
20
|
+
STATUS_SEE_OTHER = 303
|
21
|
+
STATUS_SEE_OTHER_307 = 307
|
22
|
+
STATUS_NOT_MODIFIED = 304
|
23
|
+
STATUS_BAD_REQUEST = 400
|
24
|
+
STATUS_AUTH_REQUIRED = 401
|
25
|
+
STATUS_FORBIDDEN = 403
|
26
|
+
STATUS_NOT_FOUND = 404
|
27
|
+
STATUS_METHOD_NOT_ALLOWED = 405
|
28
|
+
STATUS_NOT_ACCEPTABLE = 406
|
29
|
+
STATUS_LENGTH_REQUIRED = 411
|
30
|
+
STATUS_PRECONDITION_FAILED = 412
|
31
|
+
STATUS_SERVER_ERROR = 500
|
32
|
+
STATUS_NOT_IMPLEMENTED = 501
|
33
|
+
STATUS_BAD_GATEWAY = 502
|
34
|
+
STATUS_VARIANT_ALSO_VARIES = 506
|
35
|
+
|
36
|
+
# Hash to allow id to description maping.
|
37
|
+
|
38
|
+
STATUS_STRINGS = {
|
39
|
+
200 => "OK",
|
40
|
+
206 => "Partial Content",
|
41
|
+
300 => "Multiple Choices",
|
42
|
+
301 => "Moved Permanently",
|
43
|
+
302 => "Found",
|
44
|
+
303 => "See other", # gmosx: VERIFY THIS
|
45
|
+
304 => "Not Modified",
|
46
|
+
307 => "See other 07", # gmosx: VERIFY THIS
|
47
|
+
400 => "Bad Request",
|
48
|
+
401 => "Authorization Required",
|
49
|
+
403 => "Forbidden",
|
50
|
+
404 => "Not Found",
|
51
|
+
405 => "Method Not Allowed",
|
52
|
+
406 => "Not Acceptable",
|
53
|
+
411 => "Length Required",
|
54
|
+
412 => "Precondition Failed",
|
55
|
+
500 => "Internal Server Error",
|
56
|
+
501 => "Method Not Implemented",
|
57
|
+
502 => "Bad Gateway",
|
58
|
+
506 => "Variant Also Negotiates"
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
data/lib/raw/compiler.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
require "raw/compiler/reloader"
|
2
|
+
|
3
|
+
module Raw
|
4
|
+
|
5
|
+
# The Compiler dynamically generates action methods for the
|
6
|
+
# Controllers.
|
7
|
+
|
8
|
+
class Compiler
|
9
|
+
|
10
|
+
# A collection of the loaded template files.
|
11
|
+
|
12
|
+
attr_accessor :templates
|
13
|
+
|
14
|
+
# The reloader monitors and reloads code and template files.
|
15
|
+
|
16
|
+
attr_accessor :reloader
|
17
|
+
|
18
|
+
# Initialize the compiler.
|
19
|
+
|
20
|
+
def initialize(application)
|
21
|
+
@application = application
|
22
|
+
@reloader = Reloader.new(application)
|
23
|
+
@templates = []
|
24
|
+
end
|
25
|
+
|
26
|
+
# This method compiles missing Controller methods. Only handles
|
27
|
+
# xxx___super and xxx___{format}___view methods.
|
28
|
+
|
29
|
+
def compile(controller, meth)
|
30
|
+
meth = meth.to_s
|
31
|
+
|
32
|
+
if meth =~ %r{___super$}
|
33
|
+
compile_super(controller, meth)
|
34
|
+
elsif meth =~ %r{___view$}
|
35
|
+
compile_view(controller, meth)
|
36
|
+
else
|
37
|
+
return false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Compile the action super-method for the given URI. This
|
42
|
+
# super-method calls the action and view sub-methods. The
|
43
|
+
# action sub-method is typically provided by the controller.
|
44
|
+
#
|
45
|
+
# In a sense, the super-method orchestrates the action and
|
46
|
+
# view sub-methods to handle the given URI.
|
47
|
+
|
48
|
+
def compile_super(controller, meth)
|
49
|
+
action = meth.gsub(%r{___super$}, "")
|
50
|
+
|
51
|
+
Logger.debug "Compiling '#{action}' super-method" if $DBG
|
52
|
+
|
53
|
+
if controller.action_or_template?(action, Context.current.format)
|
54
|
+
|
55
|
+
# This is the actual control super method that calls
|
56
|
+
# the action code and the template for this format.
|
57
|
+
|
58
|
+
code = lambda do |params|
|
59
|
+
@context.format.before_action(self, @context)
|
60
|
+
|
61
|
+
@action = action.to_sym
|
62
|
+
|
63
|
+
# Call the action sub-method.
|
64
|
+
|
65
|
+
send(action, *params) if respond_to? action
|
66
|
+
|
67
|
+
# Call the view sub-method (render the template).
|
68
|
+
|
69
|
+
send("#{action}___#{@context.format}___view")
|
70
|
+
|
71
|
+
@context.format.after_action(self, @context)
|
72
|
+
|
73
|
+
cache_output()
|
74
|
+
end # lambda
|
75
|
+
|
76
|
+
controller.send(:define_method, meth, code)
|
77
|
+
controller.send(:private, meth)
|
78
|
+
|
79
|
+
return true
|
80
|
+
else
|
81
|
+
return false
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Compile the view sub-method for the given URI.
|
86
|
+
#--
|
87
|
+
# The sub-method is always generated, if no template is found
|
88
|
+
# it is just a nop method.
|
89
|
+
#
|
90
|
+
# TODO: cache the generated files (reuse the cached files
|
91
|
+
# to extract error regions)
|
92
|
+
#++
|
93
|
+
|
94
|
+
def compile_view(controller, meth)
|
95
|
+
md = meth.match(%r{(.*)___(.*)___view})
|
96
|
+
action, format = md[1], md[2]
|
97
|
+
|
98
|
+
format = @application.dispatcher.formats[format]
|
99
|
+
|
100
|
+
Logger.debug "Compiling '#{action}' view sub-method [format: #{format}]" if $DBG
|
101
|
+
|
102
|
+
unless controller.instance_methods.include? meth
|
103
|
+
# The view method is missing. The Compiler will try to
|
104
|
+
# use a template or generate an empty view method.
|
105
|
+
|
106
|
+
template = nil
|
107
|
+
|
108
|
+
if path = controller.template_path(action, format)
|
109
|
+
# Keep a ref of this template for the reloader.
|
110
|
+
|
111
|
+
@templates = @templates.push(path).uniq
|
112
|
+
|
113
|
+
# Read the template source.
|
114
|
+
|
115
|
+
template = File.read(path)
|
116
|
+
|
117
|
+
# Apply the template filters.
|
118
|
+
|
119
|
+
unless template.blank?
|
120
|
+
template = format.filter_template(template)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
controller.class_eval <<-EOCODE # , path, 0
|
125
|
+
def #{meth}
|
126
|
+
#{template}
|
127
|
+
end
|
128
|
+
EOCODE
|
129
|
+
|
130
|
+
controller.send(:private, meth)
|
131
|
+
end
|
132
|
+
|
133
|
+
return true
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Raw
|
2
|
+
|
3
|
+
# Cleanup the xhtml code generated by REXML used in other
|
4
|
+
# filters.
|
5
|
+
|
6
|
+
class CleanupFilter
|
7
|
+
|
8
|
+
def apply(source)
|
9
|
+
source = source.dup
|
10
|
+
|
11
|
+
elements = "input|img|br|hr|link|style|render|include|inject|base|meta"
|
12
|
+
source.gsub! /<textarea ([^>]*)><\/textarea>/, '<textarea \1>#{}</textarea>'
|
13
|
+
source.gsub! /<(#{elements}) ([^>]*)><\/\1>/, '<\1 \2 />'
|
14
|
+
source.gsub! /<(#{elements})><\/\1>/, '<\1 />'
|
15
|
+
|
16
|
+
return source
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
require "rexml/document"
|
2
|
+
require "rexml/streamlistener"
|
3
|
+
|
4
|
+
require "facets/core/kernel/constant"
|
5
|
+
|
6
|
+
require "raw/compiler/filter/elements/element"
|
7
|
+
|
8
|
+
module Raw
|
9
|
+
|
10
|
+
# A filter that transforms user defined Elements. Elements
|
11
|
+
# are custom tags that are used as macros or to implement
|
12
|
+
# skins.
|
13
|
+
#
|
14
|
+
# === Example
|
15
|
+
#
|
16
|
+
# <Page>
|
17
|
+
# the
|
18
|
+
# </Page>
|
19
|
+
|
20
|
+
class ElementsFilter
|
21
|
+
|
22
|
+
class Listener # :nodoc: all
|
23
|
+
include REXML::StreamListener
|
24
|
+
|
25
|
+
PREFIX_RE = /^#{Element.prefix}:/
|
26
|
+
CAPITALIZED_RE = /^[A-Z]/
|
27
|
+
|
28
|
+
attr_accessor :buffer
|
29
|
+
|
30
|
+
def initialize
|
31
|
+
super()
|
32
|
+
@buffer = ''
|
33
|
+
@stack = []
|
34
|
+
end
|
35
|
+
|
36
|
+
def tag_start(name, attributes)
|
37
|
+
if klass = is_element?(name)
|
38
|
+
obj = klass.new
|
39
|
+
|
40
|
+
attributes.each do | k, v |
|
41
|
+
obj.instance_variable_set("@#{k}", v)
|
42
|
+
end
|
43
|
+
|
44
|
+
@stack.push [obj, @buffer, @parent]
|
45
|
+
|
46
|
+
@buffer = obj._text
|
47
|
+
@parent.add_child(obj) if @parent
|
48
|
+
|
49
|
+
@parent = obj
|
50
|
+
else # This is a static element.
|
51
|
+
attrs = []
|
52
|
+
|
53
|
+
attributes.each do | k, v |
|
54
|
+
attrs << %|#{k}="#{v}"|
|
55
|
+
end
|
56
|
+
|
57
|
+
attrs = attrs.empty? ? nil : " #{attrs.join(' ')}"
|
58
|
+
|
59
|
+
@buffer << "<#{name}#{attrs}>"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def tag_end(name)
|
64
|
+
if is_element? name
|
65
|
+
obj, @buffer, @parent = @stack.pop
|
66
|
+
@buffer << obj.render
|
67
|
+
else
|
68
|
+
@buffer << "</#{name}>"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Check if a tag is an Element. If found, it also tries to
|
73
|
+
# auto-extend the klass. Returns the Element class if found.
|
74
|
+
#
|
75
|
+
# Tries many classes in the following order:
|
76
|
+
#
|
77
|
+
# * Controller::XXX
|
78
|
+
# * {controller.ann(:self, :element_namespace)}::XXX
|
79
|
+
# * Nitro::Element::XXX
|
80
|
+
|
81
|
+
def is_element?(name)
|
82
|
+
controller = Controller.current
|
83
|
+
|
84
|
+
return false unless name =~ PREFIX_RE or name =~ CAPITALIZED_RE
|
85
|
+
|
86
|
+
name = name.gsub(PREFIX_RE,'').camelize if name =~ PREFIX_RE
|
87
|
+
|
88
|
+
# Try to use Controller::xxx
|
89
|
+
# gmosx, THINK: this looks a bit dangerous to me!
|
90
|
+
|
91
|
+
begin
|
92
|
+
# gmosx, FIXME: Class.by_name also returns top level
|
93
|
+
# classes, how can we fix this?
|
94
|
+
|
95
|
+
klass = constant("#{controller}::#{name}")
|
96
|
+
rescue
|
97
|
+
# drink it!
|
98
|
+
end
|
99
|
+
|
100
|
+
# Try to use the Controller's :element_namespace annotation
|
101
|
+
|
102
|
+
if namespace = controller.ann(:self, :element_namespace)
|
103
|
+
begin
|
104
|
+
klass = constant("#{namespace}::#{name}")
|
105
|
+
rescue
|
106
|
+
# drink it!
|
107
|
+
end
|
108
|
+
end unless klass
|
109
|
+
|
110
|
+
# Try to use Nitro::Element::xxx then ::xxx
|
111
|
+
|
112
|
+
begin
|
113
|
+
klass = constant("Nitro::Element::#{name}")
|
114
|
+
rescue
|
115
|
+
# drink it!
|
116
|
+
end unless klass
|
117
|
+
|
118
|
+
return false unless klass.kind_of? Class
|
119
|
+
|
120
|
+
# Try to auto-extend.
|
121
|
+
|
122
|
+
unless klass.ancestor? Element
|
123
|
+
if Element.auto_extend
|
124
|
+
klass.send(:include, ElementMixin)
|
125
|
+
else
|
126
|
+
return false
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
return klass
|
131
|
+
end
|
132
|
+
|
133
|
+
def text(str)
|
134
|
+
@buffer << str
|
135
|
+
end
|
136
|
+
|
137
|
+
def instruction(name, attributes)
|
138
|
+
@buffer << "<?#{name}#{attributes}?>"
|
139
|
+
end
|
140
|
+
|
141
|
+
def cdata(content)
|
142
|
+
@buffer << "<![CDATA[#{content}]]>"
|
143
|
+
end
|
144
|
+
|
145
|
+
def comment(c)
|
146
|
+
unless Template.strip_xml_comments
|
147
|
+
@buffer << "<!--#{c}-->"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def doctype(name, pub_sys, long_name, uri)
|
152
|
+
@buffer << "<!DOCTYPE #{name} #{pub_sys} #{long_name} #{uri}>\n"
|
153
|
+
end
|
154
|
+
end # Listener
|
155
|
+
|
156
|
+
# Apply the filter.
|
157
|
+
|
158
|
+
def apply(source)
|
159
|
+
listen = Listener.new
|
160
|
+
REXML::Document.parse_stream(source, listen)
|
161
|
+
return listen.buffer
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
require "facets/core/string/demodulize"
|
2
|
+
require "facets/core/string/capitalized"
|
3
|
+
require "facets/core/string/camelize"
|
4
|
+
require "facets/core/class/method_name"
|
5
|
+
require "facets/core/dir/self/recurse"
|
6
|
+
require "facets/more/settings"
|
7
|
+
|
8
|
+
module Raw
|
9
|
+
|
10
|
+
# A programmatically generated element. Elements are a form
|
11
|
+
# of macros to allow for cleaner templates. They are evaluated
|
12
|
+
# at compile time, so there is no performance hit when you use
|
13
|
+
# them (at the expense of slightly reduced functionality).
|
14
|
+
#
|
15
|
+
# Nitro provides an additional method of defining elements.
|
16
|
+
# Instead of creating a lot of small classes, you can put
|
17
|
+
# .htmlx templates in the Element template_root. These templates
|
18
|
+
# are automatically converted into Element classes.
|
19
|
+
#
|
20
|
+
# For extra safety, you are advised to place your classes in the
|
21
|
+
# Nitro::Element namespace. If your classes do not extend
|
22
|
+
# Nitro::Element, the Nitro::ElementMixin is automatically
|
23
|
+
# injected in the class.
|
24
|
+
#
|
25
|
+
# An element can have and access a hierarchy of sub-elements.
|
26
|
+
# use #{content :sub_element_name} to access the render output
|
27
|
+
# of the subelement. Additionaly you can access the whole
|
28
|
+
# subelement object: _children[:sub_element_name]
|
29
|
+
#
|
30
|
+
# === Design
|
31
|
+
#
|
32
|
+
# An underscore is used for the standard attibutes to avoid name
|
33
|
+
# clashes.
|
34
|
+
|
35
|
+
module ElementMixin
|
36
|
+
# The parent of this element.
|
37
|
+
|
38
|
+
attr_accessor :_parent
|
39
|
+
|
40
|
+
# The children of this element.
|
41
|
+
|
42
|
+
attr_accessor :_children
|
43
|
+
alias_method :children, :_children
|
44
|
+
|
45
|
+
# The text of this element.
|
46
|
+
|
47
|
+
attr_accessor :_text
|
48
|
+
|
49
|
+
# The view of this element.
|
50
|
+
|
51
|
+
attr_accessor :_view
|
52
|
+
|
53
|
+
# The id of this element.
|
54
|
+
|
55
|
+
attr_accessor :id
|
56
|
+
|
57
|
+
def initialize(*args)
|
58
|
+
@_children = {}
|
59
|
+
@_text = ''
|
60
|
+
@id = self.class.to_s.demodulize.underscore
|
61
|
+
end
|
62
|
+
|
63
|
+
# Prepend this code to the element content.
|
64
|
+
|
65
|
+
def open
|
66
|
+
end
|
67
|
+
|
68
|
+
# If an optional name parameter is passed renders
|
69
|
+
# the content of the named child element.
|
70
|
+
#
|
71
|
+
# eg. #{content :child_element_id}
|
72
|
+
#
|
73
|
+
# === Example
|
74
|
+
#
|
75
|
+
# <Page>
|
76
|
+
# ..
|
77
|
+
#
|
78
|
+
# <Box id="hello">
|
79
|
+
# ..
|
80
|
+
# </Box>
|
81
|
+
#
|
82
|
+
# <Box id="world">
|
83
|
+
# ..
|
84
|
+
# </Box>
|
85
|
+
#
|
86
|
+
# <Sidebar>
|
87
|
+
# ..
|
88
|
+
# </Sidebar>
|
89
|
+
#
|
90
|
+
# ..
|
91
|
+
#
|
92
|
+
# </Page>
|
93
|
+
#
|
94
|
+
# Access children content from within the enclosing element
|
95
|
+
# (Page) like this:
|
96
|
+
#
|
97
|
+
# {content :hello}
|
98
|
+
# {content :world}
|
99
|
+
# {content :sidebar}
|
100
|
+
|
101
|
+
def content(cname = nil)
|
102
|
+
if cname
|
103
|
+
if c = @_children[cname.to_s]
|
104
|
+
c.content
|
105
|
+
else
|
106
|
+
return nil
|
107
|
+
end
|
108
|
+
else
|
109
|
+
@_text
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Include a text file in the element template. All the
|
114
|
+
# conventions of the StaticInclude compiler apply here. Please
|
115
|
+
# not that unless you pass an absolute path (starting with
|
116
|
+
# '/') you have to pass a controller instance as well.
|
117
|
+
#
|
118
|
+
# === Example:
|
119
|
+
#
|
120
|
+
# def render
|
121
|
+
# %~
|
122
|
+
# <div>
|
123
|
+
# #{include '/links/latest'}
|
124
|
+
# </div>
|
125
|
+
# ~
|
126
|
+
# end
|
127
|
+
|
128
|
+
def include(href, controller = nil)
|
129
|
+
filename = StaticIncludeFilter.new.resolve_include_filename(href)
|
130
|
+
return File.read(filename)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Append this code to the element content.
|
134
|
+
|
135
|
+
def close
|
136
|
+
end
|
137
|
+
|
138
|
+
# Override this.
|
139
|
+
|
140
|
+
def render
|
141
|
+
"#{open}#{content}#{close}"
|
142
|
+
end
|
143
|
+
|
144
|
+
def render_children
|
145
|
+
str = ''
|
146
|
+
for c in @_children.values
|
147
|
+
str << c.render
|
148
|
+
end
|
149
|
+
|
150
|
+
return str
|
151
|
+
end
|
152
|
+
|
153
|
+
def add_child(child)
|
154
|
+
child._parent = self
|
155
|
+
@_children[child.instance_variable_get('@id')] = child
|
156
|
+
end
|
157
|
+
|
158
|
+
alias_method :children, :_children
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
# A programmatically generated element.
|
163
|
+
#
|
164
|
+
# === Usage
|
165
|
+
#
|
166
|
+
# = in the code
|
167
|
+
#
|
168
|
+
# class Page < Nitro::Element
|
169
|
+
# def render
|
170
|
+
# %{
|
171
|
+
# <div id="@id">#{content}</div>
|
172
|
+
# }
|
173
|
+
# end
|
174
|
+
# end
|
175
|
+
#
|
176
|
+
# = in your template
|
177
|
+
#
|
178
|
+
# <Page>hello</Page>
|
179
|
+
#
|
180
|
+
# => <div id="page">hello</div>
|
181
|
+
#
|
182
|
+
# the id is automatically fille with the class name using
|
183
|
+
# class.method_name eg. MyModule::MyPage => my_module__my_page
|
184
|
+
#
|
185
|
+
# you can override the id to use the element multiple times on
|
186
|
+
# the page.
|
187
|
+
#
|
188
|
+
# == Sub Elements
|
189
|
+
#
|
190
|
+
# Elements can be imbricated. To render the the child element in
|
191
|
+
# the parent's template, use #{content :element_id}
|
192
|
+
#
|
193
|
+
# === Design
|
194
|
+
#
|
195
|
+
# An underscore is used for the standard attibutes to avoid name
|
196
|
+
# clashes.
|
197
|
+
|
198
|
+
class Element
|
199
|
+
include ElementMixin
|
200
|
+
|
201
|
+
# The prefix for element tags (in xhtml compatibility mode)
|
202
|
+
|
203
|
+
setting :prefix, :default => 'x', :doc => 'The prefix for element tags'
|
204
|
+
|
205
|
+
# Allow auto extension of element classes?
|
206
|
+
|
207
|
+
setting :auto_extend, :default => true, :doc => 'Allow auto extension of element classes?'
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|