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,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
@@ -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
@@ -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