trellis 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/History.txt +20 -3
- data/Manifest.txt +3 -0
- data/README.txt +156 -0
- data/Rakefile +3 -0
- data/config/hoe.rb +25 -20
- data/lib/trellis/trellis.rb +25 -10
- data/lib/trellis/utils.rb +24 -6
- data/lib/trellis/version.rb +1 -1
- data/test/application_spec.rb +16 -1
- data/test/fixtures/application_spec_applications.rb +80 -1
- data/test/fixtures/component_spec_components.rb +114 -0
- data/test/page_spec.rb +38 -1
- data/test/renderer_spec.rb +23 -4
- data/test/router_spec.rb +1 -1
- metadata +61 -17
data/.gitignore
ADDED
data/History.txt
CHANGED
@@ -1,6 +1,23 @@
|
|
1
|
-
== 0.0.
|
1
|
+
== 0.0.5
|
2
|
+
* 2 major enhancements:
|
3
|
+
* template can now use erb (erubis using Processing Instructions (PI))
|
4
|
+
* added check for nil Rack::Session when using Cookie Store
|
5
|
+
|
6
|
+
== 0.0.4
|
7
|
+
* 1 major enhancement:
|
8
|
+
* tests for Renderer
|
9
|
+
* more routing tests
|
10
|
+
* request query params now available to page as instance variables
|
11
|
+
* cleaned up dependencies
|
2
12
|
|
3
|
-
|
13
|
+
== 0.0.3
|
14
|
+
* 1 major enhancement:
|
15
|
+
* stand-in pages
|
16
|
+
|
17
|
+
== 0.0.2 2009-09-19
|
18
|
+
* 5 major enhancements:
|
19
|
+
* code reloading
|
20
|
+
* template reloading
|
4
21
|
* event subsystem revamped
|
5
22
|
* custom routing
|
6
23
|
* more tests!
|
@@ -8,4 +25,4 @@
|
|
8
25
|
== 0.0.1 2009-08-29
|
9
26
|
|
10
27
|
* 1 major enhancement:
|
11
|
-
*
|
28
|
+
* initial release
|
data/Manifest.txt
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
.gitignore
|
1
2
|
History.txt
|
2
3
|
License.txt
|
3
4
|
Manifest.txt
|
4
5
|
README
|
6
|
+
README.txt
|
5
7
|
Rakefile
|
6
8
|
config/hoe.rb
|
7
9
|
config/requirements.rb
|
@@ -109,6 +111,7 @@ test/component_spec.rb
|
|
109
111
|
test/core_extensions_spec.rb
|
110
112
|
test/default_router_spec.rb
|
111
113
|
test/fixtures/application_spec_applications.rb
|
114
|
+
test/fixtures/component_spec_components.rb
|
112
115
|
test/page_spec.rb
|
113
116
|
test/renderer_spec.rb
|
114
117
|
test/router_spec.rb
|
data/README.txt
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
= Project: Trellis
|
2
|
+
|
3
|
+
Trellis is a component-based Web Application Framework written in Ruby.
|
4
|
+
Its main goal is to bring component-driven development to the micro-framework world.
|
5
|
+
The framework aims to be a zero-configuration framework.
|
6
|
+
|
7
|
+
It draws inspiration from:
|
8
|
+
|
9
|
+
Ruby Web Frameworks
|
10
|
+
===================
|
11
|
+
* Rails
|
12
|
+
* Camping
|
13
|
+
* Merb
|
14
|
+
* Iowa
|
15
|
+
* Sinatra
|
16
|
+
|
17
|
+
Java Web Frameworks
|
18
|
+
===================
|
19
|
+
* Tapesty
|
20
|
+
* Wicket
|
21
|
+
* The good parts of JSF (components)
|
22
|
+
|
23
|
+
Others
|
24
|
+
======
|
25
|
+
* Seaside
|
26
|
+
|
27
|
+
== Goals
|
28
|
+
Accomplished goals are marked with a (*)
|
29
|
+
- * Pure HTML templates or in-line template creation with Markaby or HAML
|
30
|
+
- * To abstract away the request-response nature of web development and replace
|
31
|
+
with events and listeners
|
32
|
+
- * Reusable, extensible components including invisible components (behavior
|
33
|
+
providers), tags (stateless components) or stateful components
|
34
|
+
- Easy component composition and markup inheritance
|
35
|
+
- * Multi-threading support
|
36
|
+
- * Heavy CoC :-) (Convention Over Configuration) ala Rails
|
37
|
+
- * No static code generation, no generators, just a Ruby file!
|
38
|
+
- * Component Libraries as Gems
|
39
|
+
- * Solid Ajax support attached to the event model using a single* massively tested
|
40
|
+
Ajax framework/toolkit such as JQuery
|
41
|
+
- Skinnable components a la DotNet. That is the ability to apply a theme to a
|
42
|
+
page and cascade that to the contained components
|
43
|
+
- Support for continuations in a componentized fashion (maybe)
|
44
|
+
- CRUD behaviours for Pages/Components and using Datamapper under the covers
|
45
|
+
- Web-based debugging and administration of the application similar to what
|
46
|
+
Seaside provides
|
47
|
+
|
48
|
+
== Development Goals (To-do's)
|
49
|
+
|
50
|
+
- Keep the core framework in a single file
|
51
|
+
- Keep the core components in another file
|
52
|
+
- I have not done anything about exception handling (didn't wanted to litter the
|
53
|
+
code base). Currently I'm always sending an HTTP 200 back or I'm just letting
|
54
|
+
the app blow chuncks and showing rack's exception page
|
55
|
+
- Radius usage is really entrenched in the current component implementation need
|
56
|
+
to clean it up
|
57
|
+
- Currently Trellis uses the Mongrel Rack Adapter. In the near future the
|
58
|
+
particular web server would be configurable (one of the reasons to use Rack)
|
59
|
+
|
60
|
+
== Classes
|
61
|
+
|
62
|
+
Trellis::Application:: Base case for all Trellis applications
|
63
|
+
Trellis::Page:: Base class for all application Pages
|
64
|
+
Trellis::Renderer:: Renders XML/XHTML tags in the markup using Radius
|
65
|
+
Trellis::Component:: Base class for all Trellis components (work in progress)
|
66
|
+
Trellis::DefaultRouter:: Encapsulates the default routing logic
|
67
|
+
|
68
|
+
== <b>Notes</b>::
|
69
|
+
|
70
|
+
* Event model is pretty shallow right now. Basically events are just
|
71
|
+
a convention of how the URLs are interpreted which results on a page method
|
72
|
+
being invoked. A given URL contains information about the target page, event,
|
73
|
+
source of the event and possible context values.This information is used to
|
74
|
+
map to a single method in the page.
|
75
|
+
* Navigation is a la Tapestry; Page methods can return the instance of a
|
76
|
+
injected Page or they can return self in which case the same page is
|
77
|
+
re-rendered.
|
78
|
+
* Component can register their event handlers with the page. The page wraps the
|
79
|
+
event handlers of the contained components and dispatches the events to the
|
80
|
+
component instance.
|
81
|
+
* Currently the Application is a single object, the multi-threading (which I had
|
82
|
+
nothing to do with is provided by Rack) happens in the request dispatching.
|
83
|
+
* Trellis is not a managed framework like Tapestry, in that sense it is more like
|
84
|
+
Wicket. Pages instances are just created when needed, there is no pooling,
|
85
|
+
or any of the complexity involved in activating/passivating objects to a pool.
|
86
|
+
|
87
|
+
== Installation
|
88
|
+
|
89
|
+
* <tt>gem install trellis</tt>
|
90
|
+
|
91
|
+
A Trellis application consists of the Application class; a descendant of
|
92
|
+
Trellis::Application and one or more pages; descendants of Trellis::Page. The
|
93
|
+
Application at a minimum needs to declare the starting or home page:
|
94
|
+
|
95
|
+
module Hello
|
96
|
+
class HelloWorld < Trellis::Application
|
97
|
+
home :home
|
98
|
+
end
|
99
|
+
|
100
|
+
class Home < Trellis::Page
|
101
|
+
template do html { body { h1 "Hello World!" }} end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
To run the above application simply add the line:
|
106
|
+
|
107
|
+
Hello::HelloWorld.new.start
|
108
|
+
|
109
|
+
That will start the application on Mongrel running on port 3000 by default. To
|
110
|
+
run on any other port pass the port number to the start method like:
|
111
|
+
|
112
|
+
Hello::HelloWorld.new.start 8282
|
113
|
+
|
114
|
+
== Required Gems
|
115
|
+
|
116
|
+
rack => http://rack.rubyforge.org
|
117
|
+
mongrel => http://mongrel.rubyforge.org
|
118
|
+
radius => http://radius.rubyforge.org
|
119
|
+
builder => http://builder.rubyforge.org
|
120
|
+
paginator => http://paginator.rubyforge.org
|
121
|
+
hpricot => http://code.whytheluckystiff.net/hpricot
|
122
|
+
extensions => http://extensions.rubyforge.org
|
123
|
+
haml => http://haml.hamptoncatlin.com
|
124
|
+
markaby => http://code.whytheluckystiff.net/markaby
|
125
|
+
|
126
|
+
== LICENSE:
|
127
|
+
|
128
|
+
(The MIT License)
|
129
|
+
|
130
|
+
Copyright &169;2001-2009 Integrallis Software, LLC.
|
131
|
+
|
132
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
133
|
+
a copy of this software and associated documentation files (the
|
134
|
+
'Software'), to deal in the Software without restriction, including
|
135
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
136
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
137
|
+
permit persons to whom the Software is furnished to do so, subject to
|
138
|
+
the following conditions:
|
139
|
+
|
140
|
+
The above copyright notice and this permission notice shall be
|
141
|
+
included in all copies or substantial portions of the Software.
|
142
|
+
|
143
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
144
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
145
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
146
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
147
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
148
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
149
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
150
|
+
|
151
|
+
== Contact
|
152
|
+
|
153
|
+
Author:: Brian Sam-Bodden
|
154
|
+
Email:: bsbodden@integrallis.com
|
155
|
+
Home Page:: http://integrallis.com
|
156
|
+
License:: MIT Licence (http://www.opensource.org/licenses/mit-license.html)
|
data/Rakefile
CHANGED
data/config/hoe.rb
CHANGED
@@ -9,20 +9,24 @@ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
|
9
9
|
DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
|
10
10
|
EXTRA_DEPENDENCIES = [
|
11
11
|
['paginator', '>= 1.1.1'],
|
12
|
-
['rack', '>= 0.
|
13
|
-
['radius', '>= 0.
|
12
|
+
['rack', '>= 1.0.1'],
|
13
|
+
['radius', '>= 0.6.1'],
|
14
14
|
['builder', '>= 2.1.2'],
|
15
15
|
['hpricot', '>= 0.6.0'],
|
16
16
|
['extensions', '>= 0.6.0'],
|
17
|
-
['haml', '>= 2.
|
18
|
-
['RedCloth', '>= 4.
|
19
|
-
['
|
17
|
+
['haml', '>= 2.2.9'],
|
18
|
+
['RedCloth', '>= 4.2.2'],
|
19
|
+
['bluecloth', '>= 2.0.5'],
|
20
20
|
['markaby', '>= 0.5'],
|
21
|
-
['log4r', '>= 1.
|
22
|
-
['
|
23
|
-
['
|
24
|
-
['
|
25
|
-
['
|
21
|
+
['log4r', '>= 1.1.2'],
|
22
|
+
['facets', '>= 2.7.0'],
|
23
|
+
['directory_watcher', '>= 1.2.0'],
|
24
|
+
['rack-cache', '>= 0.5.2'],
|
25
|
+
['rack-contrib', '>= 0.9.2'],
|
26
|
+
['rack-test', '>= 0.5.0'],
|
27
|
+
['erubis', '>= 2.6.5'],
|
28
|
+
['rspec', '>= 1.2.9'],
|
29
|
+
['newgem', '>= 1.5.2']
|
26
30
|
] # An array of rubygem dependencies [name, version]
|
27
31
|
|
28
32
|
@config_file = "~/.rubyforge/user-config.yml"
|
@@ -62,18 +66,19 @@ end
|
|
62
66
|
|
63
67
|
# Generate all the Rake tasks
|
64
68
|
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
65
|
-
$hoe = Hoe.
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
69
|
+
$hoe = Hoe.spec GEM_NAME do
|
70
|
+
self.version = VERS
|
71
|
+
self.developer(AUTHOR, EMAIL)
|
72
|
+
self.description = DESCRIPTION
|
73
|
+
self.summary = DESCRIPTION
|
74
|
+
self.url = HOMEPATH
|
75
|
+
self.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
|
76
|
+
self.test_globs = ["test/**/test_*.rb"]
|
77
|
+
self.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
|
73
78
|
|
74
79
|
# == Optional
|
75
|
-
|
76
|
-
|
80
|
+
self.changes = paragraphs_of("History.txt", 0..1).join("\n\n")
|
81
|
+
self.extra_deps = EXTRA_DEPENDENCIES
|
77
82
|
end
|
78
83
|
|
79
84
|
CHANGES = $hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
|
data/lib/trellis/trellis.rb
CHANGED
@@ -37,9 +37,9 @@ require 'haml'
|
|
37
37
|
require 'markaby'
|
38
38
|
require 'redcloth'
|
39
39
|
require 'bluecloth'
|
40
|
-
require 'english/inflect'
|
41
40
|
require 'facets'
|
42
41
|
require 'directory_watcher'
|
42
|
+
require 'erubis'
|
43
43
|
|
44
44
|
module Trellis
|
45
45
|
|
@@ -116,7 +116,7 @@ module Trellis
|
|
116
116
|
|
117
117
|
Application.logger.debug "request received with url_root of #{request.script_name}" if request.script_name
|
118
118
|
|
119
|
-
session = env[
|
119
|
+
session = env['rack.session'] ||= {}
|
120
120
|
|
121
121
|
router = find_router_for(request)
|
122
122
|
route = router.route(request)
|
@@ -239,6 +239,7 @@ module Trellis
|
|
239
239
|
else
|
240
240
|
{}
|
241
241
|
end
|
242
|
+
params << request.params
|
242
243
|
params.each_pair { |name, value| page.instance_variable_set("@#{name}".to_sym, value) }
|
243
244
|
end
|
244
245
|
end
|
@@ -315,7 +316,7 @@ module Trellis
|
|
315
316
|
# components in the same page or other pages
|
316
317
|
class Page
|
317
318
|
|
318
|
-
TEMPLATE_FORMATS = [:html, :xhtml, :haml, :textile, :markdown]
|
319
|
+
TEMPLATE_FORMATS = [:html, :xhtml, :haml, :textile, :markdown, :eruby]
|
319
320
|
|
320
321
|
@@subclasses = Hash.new
|
321
322
|
@@template_registry = Hash.new
|
@@ -342,19 +343,19 @@ module Trellis
|
|
342
343
|
end
|
343
344
|
|
344
345
|
def self.template(body = nil, options = nil, &block)
|
345
|
-
format = options[:format] if options
|
346
|
+
@format = (options[:format] if options) || :html
|
346
347
|
if block_given?
|
347
348
|
mab = Markaby::Builder.new({}, self, &block)
|
348
349
|
html = mab.to_s
|
349
350
|
else
|
350
|
-
case format
|
351
|
+
case @format
|
351
352
|
when :haml
|
352
353
|
html = Haml::Engine.new(body).render
|
353
354
|
when :textile
|
354
355
|
html = RedCloth.new(body).to_html
|
355
356
|
when :markdown
|
356
|
-
html = BlueCloth.new(body).to_html
|
357
|
-
else # assume the body is (x)html
|
357
|
+
html = "<html><body>#{BlueCloth.new(body).to_html}</body></html>"
|
358
|
+
else # assume the body is (x)html, also eruby is treated as (x)html at this point
|
358
359
|
html = body
|
359
360
|
end
|
360
361
|
end
|
@@ -371,6 +372,10 @@ module Trellis
|
|
371
372
|
end
|
372
373
|
@template
|
373
374
|
end
|
375
|
+
|
376
|
+
def self.format
|
377
|
+
@format
|
378
|
+
end
|
374
379
|
|
375
380
|
def self.pages(*syms)
|
376
381
|
@pages = @pages | syms
|
@@ -408,7 +413,7 @@ module Trellis
|
|
408
413
|
unless value
|
409
414
|
method_result = send method.to_sym
|
410
415
|
else
|
411
|
-
method_result = send method.to_sym, value
|
416
|
+
method_result = send method.to_sym, Rack::Utils.unescape(value)
|
412
417
|
end
|
413
418
|
|
414
419
|
# determine navigation flow based on the return value of the method call
|
@@ -614,6 +619,8 @@ module Trellis
|
|
614
619
|
def initialize(page)
|
615
620
|
@page = page
|
616
621
|
@context = Context.new
|
622
|
+
# context for erubis templates
|
623
|
+
@eruby_context = {} if @page.class.format == :eruby
|
617
624
|
|
618
625
|
# add all instance variables in the page as values accesible from the tags
|
619
626
|
page.instance_variables.each do |var|
|
@@ -621,17 +628,19 @@ module Trellis
|
|
621
628
|
unless value.kind_of?(Trellis::Page)
|
622
629
|
sym = "#{var}=".split('@').last.to_sym
|
623
630
|
@context.globals.send(sym, value)
|
631
|
+
@eruby_context["#{var}".split('@').last] = value if @eruby_context
|
624
632
|
end
|
625
633
|
end
|
626
634
|
|
627
635
|
# add other useful values to the tag context
|
628
636
|
@context.globals.send(:page_name=, page.class.to_s)
|
637
|
+
@eruby_context[:page_name] = page.class.to_s if @eruby_context
|
629
638
|
|
630
639
|
#TODO add public page methods to the context
|
631
640
|
|
632
|
-
|
633
641
|
# add the page to the context too
|
634
642
|
@context.globals.page = page
|
643
|
+
@eruby_context[:page] = page if @eruby_context
|
635
644
|
|
636
645
|
# register the components contained in the page with the renderer's context
|
637
646
|
page.class.components.each do |component|
|
@@ -642,7 +651,13 @@ module Trellis
|
|
642
651
|
end
|
643
652
|
|
644
653
|
def render
|
645
|
-
@
|
654
|
+
unless @page.class.format == :eruby
|
655
|
+
@parser.parse(@page.class.parsed_template.to_html)
|
656
|
+
else
|
657
|
+
puts "eruby context is #{@eruby_context}"
|
658
|
+
preprocessed = Erubis::PI::Eruby.new(@page.class.parsed_template.to_html, :trim => false).evaluate(@eruby_context)
|
659
|
+
@parser.parse(preprocessed)
|
660
|
+
end
|
646
661
|
end
|
647
662
|
|
648
663
|
end # renderer
|
data/lib/trellis/utils.rb
CHANGED
@@ -24,12 +24,6 @@
|
|
24
24
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
25
|
#++
|
26
26
|
|
27
|
-
module Kernel
|
28
|
-
def with( object, &block )
|
29
|
-
object.instance_eval(&block); object
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
27
|
class Object #:nodoc:
|
34
28
|
def meta_def(m,&b) #:nodoc:
|
35
29
|
metaclass.send(:define_method,m,&b)
|
@@ -99,6 +93,30 @@ class Class #:nodoc:
|
|
99
93
|
end
|
100
94
|
|
101
95
|
class String #:nodoc:
|
96
|
+
|
97
|
+
PLURALS = [['(quiz)$', '\1zes'],['(ox)$', '\1en'],['([m|l])ouse$', '\1ice'],['(matr|vert|ind)ix|ex$', '\1ices'],
|
98
|
+
['(x|ch|ss|sh)$', '\1es'],['([^aeiouy]|qu)ies$', '\1y'],['([^aeiouy]|q)y$$', '\1ies'],['(hive)$', '\1s'],
|
99
|
+
['(?:[^f]fe|([lr])f)$', '\1\2ves'],['(sis)$', 'ses'],['([ti])um$', '\1a'],['(buffal|tomat)o$', '\1oes'],['(bu)s$', '\1es'],
|
100
|
+
['(alias|status)$', '\1es'],['(octop|vir)us$', '\1i'],['(ax|test)is$', '\1es'],['s$', 's'],['$', 's']]
|
101
|
+
SINGULARS =[['(quiz)zes$', '\1'],['(matr)ices$', '\1ix'],['(vert|ind)ices$', '\1ex'],['^(ox)en$', '\1'],['(alias|status)es$', '\1'],
|
102
|
+
['(octop|vir)i$', '\1us'],['(cris|ax|test)es$', '\1is'],['(shoe)s$', '\1'],['[o]es$', '\1'],['[bus]es$', '\1'],['([m|l])ice$', '\1ouse'],
|
103
|
+
['(x|ch|ss|sh)es$', '\1'],['(m)ovies$', '\1ovie'],['[s]eries$', '\1eries'],['([^aeiouy]|qu)ies$', '\1y'],['[lr]ves$', '\1f'],
|
104
|
+
['(tive)s$', '\1'],['(hive]s$', '\1'],['([^f]]ves$', '\1fe'],['(^analy)ses$', '\1sis'],
|
105
|
+
['([a]naly|[b]a|[d]iagno|[p]arenthe|[p]rogno|[s]ynop|[t]he)ses$', '\1\2sis'],['([ti])a$', '\1um'],['(news)$', '\1ews']]
|
106
|
+
|
107
|
+
def singular()
|
108
|
+
SINGULARS.each { |match_exp, replacement_exp| return gsub(Regexp.compile(match_exp), replacement_exp) unless match(Regexp.compile(match_exp)).nil?}
|
109
|
+
end
|
110
|
+
|
111
|
+
def plural()
|
112
|
+
PLURALS.each { |match_exp, replacement_exp| return gsub(Regexp.compile(match_exp), replacement_exp) unless match(Regexp.compile(match_exp)).nil? }
|
113
|
+
end
|
114
|
+
|
115
|
+
def plural?
|
116
|
+
PLURALS.each {|match_exp, replacement_exp| return true if match(Regexp.compile(match_exp))}
|
117
|
+
false
|
118
|
+
end
|
119
|
+
|
102
120
|
def blank?
|
103
121
|
self !~ /\S/
|
104
122
|
end
|
data/lib/trellis/version.rb
CHANGED
data/test/application_spec.rb
CHANGED
@@ -89,7 +89,7 @@ describe Trellis::Application, " requesting a route" do
|
|
89
89
|
response.body.should == '<html><body>goodbye/cruel/world</body></html>'
|
90
90
|
end
|
91
91
|
|
92
|
-
it "should supports mixing multiple
|
92
|
+
it "should supports mixing multiple splats" do
|
93
93
|
response = @request.get('/splats/bar/foo/bling/baz/boom')
|
94
94
|
response.body.should == '<html><body>barblingbaz/boom</body></html>'
|
95
95
|
|
@@ -101,4 +101,19 @@ describe Trellis::Application, " requesting a route" do
|
|
101
101
|
response = @request.get('/mixed/afoo/bar/baz')
|
102
102
|
response.body.should == '<html><body>bar/baz-afoo</body></html>'
|
103
103
|
end
|
104
|
+
|
105
|
+
it "should merge named params and query string params" do
|
106
|
+
response_mr_bean = @request.get("/hello/Bean?salutation=Mr.%20")
|
107
|
+
response_mr_bean.body.should == "<html><body><h2>Hello</h2>Mr. Bean</body></html>"
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should match a dot ('.') as part of a named param" do
|
111
|
+
response = @request.get("/foobar/user@example.com/thebar")
|
112
|
+
response.body.should == "<html><body>user@example.com-thebar</body></html>"
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should match a literal dot ('.') outside of named params" do
|
116
|
+
response = @request.get("/downloads/logo.gif")
|
117
|
+
response.body.should == "<html><body>logo-gif</body></html>"
|
118
|
+
end
|
104
119
|
end
|
@@ -22,6 +22,10 @@ module TestApp
|
|
22
22
|
def on_event3
|
23
23
|
@other
|
24
24
|
end
|
25
|
+
|
26
|
+
def on_event4(value)
|
27
|
+
"the value is #{value}"
|
28
|
+
end
|
25
29
|
end
|
26
30
|
|
27
31
|
class Other < Trellis::Page
|
@@ -61,7 +65,7 @@ module TestApp
|
|
61
65
|
html {
|
62
66
|
body {
|
63
67
|
h2 "Hello"
|
64
|
-
text %[<trellis:value name="name"/>]
|
68
|
+
text %[<trellis:value name="salutation"/><trellis:value name="name"/>]
|
65
69
|
}
|
66
70
|
}
|
67
71
|
end
|
@@ -136,4 +140,79 @@ module TestApp
|
|
136
140
|
end
|
137
141
|
end
|
138
142
|
|
143
|
+
class RoutedWithTwoParams < Trellis::Page
|
144
|
+
route '/foobar/:foo/:bar'
|
145
|
+
|
146
|
+
template do
|
147
|
+
html {
|
148
|
+
body {
|
149
|
+
text %[<trellis:value name="foo"/>]
|
150
|
+
text '-'
|
151
|
+
text %[<trellis:value name="bar"/>]
|
152
|
+
}
|
153
|
+
}
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
class RoutedWithImplicitDot < Trellis::Page
|
158
|
+
route '/downloads/:file.:ext'
|
159
|
+
|
160
|
+
template do
|
161
|
+
html {
|
162
|
+
body {
|
163
|
+
text %[<trellis:value name="file"/>]
|
164
|
+
text '-'
|
165
|
+
text %[<trellis:value name="ext"/>]
|
166
|
+
}
|
167
|
+
}
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
class SamplePage < Trellis::Page
|
172
|
+
attr_accessor :value
|
173
|
+
template do html { body { text %[<trellis:value name="value"/>] }} end
|
174
|
+
end
|
175
|
+
|
176
|
+
class AnotherSamplePage < Trellis::Page
|
177
|
+
template do html { body { text %[<trellis:value name="page_name"/>] }} end
|
178
|
+
end
|
179
|
+
|
180
|
+
class HamlPage < Trellis::Page
|
181
|
+
template %[!!! XML
|
182
|
+
!!! Strict
|
183
|
+
%html{ :xmlns => "http://www.w3.org/1999/xhtml" }
|
184
|
+
%head
|
185
|
+
%meta{ :content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type" }
|
186
|
+
%title
|
187
|
+
This is a HAML page
|
188
|
+
%body
|
189
|
+
%h1
|
190
|
+
Page Title
|
191
|
+
%p
|
192
|
+
HAML rocks!], :format => :haml
|
193
|
+
end
|
194
|
+
|
195
|
+
class TextilePage < Trellis::Page
|
196
|
+
template %[A *simple* example.], :format => :textile
|
197
|
+
end
|
198
|
+
|
199
|
+
class MarkDownPage < Trellis::Page
|
200
|
+
template "# This is the Title\n## This is the SubTitle\nThis is some text", :format => :markdown
|
201
|
+
end
|
202
|
+
|
203
|
+
class HTMLPage < Trellis::Page
|
204
|
+
template "<html><body><h1>This is just HTML</h1></body></html>"
|
205
|
+
end
|
206
|
+
|
207
|
+
class ERubyPage < Trellis::Page
|
208
|
+
attr_reader :list
|
209
|
+
|
210
|
+
def initialize
|
211
|
+
self
|
212
|
+
@list = ["Hey", "bud", "let's", "party!"]
|
213
|
+
end
|
214
|
+
|
215
|
+
template %[<html><body><ul><?rb for item in @list ?><li>@{item}@</li><?rb end ?></ul></body></html>], :format => :eruby
|
216
|
+
end
|
217
|
+
|
139
218
|
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module TestComponents
|
2
|
+
|
3
|
+
class SimpleComponent < Trellis::Component
|
4
|
+
tag_name "simple_component"
|
5
|
+
|
6
|
+
render do |tag|
|
7
|
+
"hello from simple component"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Counter < Trellis::Component
|
12
|
+
is_stateful
|
13
|
+
|
14
|
+
tag_name "counter"
|
15
|
+
|
16
|
+
field :value, :persistent => true
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
reset
|
20
|
+
end
|
21
|
+
|
22
|
+
render do |tag|
|
23
|
+
tid = tag.attr['tid']
|
24
|
+
page = tag.globals.page
|
25
|
+
counter = page.send("counter_#{tid}")
|
26
|
+
value = counter.value
|
27
|
+
href_add = Trellis::DefaultRouter.to_uri(:page => page.class.name,
|
28
|
+
:event => 'add',
|
29
|
+
:source => "counter_#{tid}")
|
30
|
+
href_subtract = Trellis::DefaultRouter.to_uri(:page => page.class.name,
|
31
|
+
:event => 'subtract',
|
32
|
+
:source => "counter_#{tid}")
|
33
|
+
builder = Builder::XmlMarkup.new
|
34
|
+
builder.div(:id => tid) {
|
35
|
+
builder.text!(value.to_s)
|
36
|
+
builder.a("++", :href => href_add)
|
37
|
+
builder.a("--", :href => href_subtract)
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def on_add
|
42
|
+
@value = @value + 1
|
43
|
+
end
|
44
|
+
|
45
|
+
def on_subtract
|
46
|
+
@value = @value - 1
|
47
|
+
end
|
48
|
+
|
49
|
+
def reset
|
50
|
+
@value = 0
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Contributions < Trellis::Component
|
55
|
+
#tag_name "contributions"
|
56
|
+
|
57
|
+
# -----------------------
|
58
|
+
# component contributions
|
59
|
+
# -----------------------
|
60
|
+
page_contribution :style_link, "/someplace/my_styles.css"
|
61
|
+
page_contribution :script_link, "/someplace/my_script.js"
|
62
|
+
page_contribution :style, %[html { color:#555555; background-color:#303030; }], :scope => :class
|
63
|
+
page_contribution :style, %[/* just a comment */], :scope => :instance
|
64
|
+
page_contribution :script, %[alert('hello from ${tid}');], :scope => :instance
|
65
|
+
page_contribution :script, %[alert('hello just once');], :scope => :class
|
66
|
+
|
67
|
+
page_contribution(:dom) {
|
68
|
+
at("body")['class'] = 'new_class'
|
69
|
+
}
|
70
|
+
|
71
|
+
render do |tag|
|
72
|
+
"hear ye, hear ye!"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class ApplicationWithComponents < Trellis::Application
|
77
|
+
home :page_with_simple_component
|
78
|
+
end
|
79
|
+
|
80
|
+
class PageWithSimpleComponent < Trellis::Page
|
81
|
+
template do html { body { text %[<trellis:simple_component/>] }} end
|
82
|
+
end
|
83
|
+
|
84
|
+
class PageWithStatefulComponent < Trellis::Page
|
85
|
+
route '/counters'
|
86
|
+
|
87
|
+
template do
|
88
|
+
html {
|
89
|
+
body {
|
90
|
+
text %[
|
91
|
+
<trellis:counter tid="one" />
|
92
|
+
<hr/>
|
93
|
+
<trellis:counter tid="two" />
|
94
|
+
<hr/>
|
95
|
+
<trellis:counter tid="three" />
|
96
|
+
]
|
97
|
+
}
|
98
|
+
}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class PageWithContributions < Trellis::Page
|
103
|
+
template do
|
104
|
+
html {
|
105
|
+
head {
|
106
|
+
title "counters"
|
107
|
+
}
|
108
|
+
body {
|
109
|
+
text %[<trellis:contributions tid="one"/><trellis:contributions tid="two"/>]
|
110
|
+
}
|
111
|
+
}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/test/page_spec.rb
CHANGED
@@ -20,11 +20,16 @@ describe Trellis::Page, " when sending an event to a page" do
|
|
20
20
|
response.body.should == "just some text"
|
21
21
|
end
|
22
22
|
|
23
|
-
it "should redirect to the injected page as a response if the event handler returns an injected page
|
23
|
+
it "should redirect to the injected page as a response if the event handler returns an injected page" do
|
24
24
|
response = @request.get("/home/events/event3")
|
25
25
|
response.status.should be(302)
|
26
26
|
response.headers['Location'].should == '/other'
|
27
27
|
end
|
28
|
+
|
29
|
+
it "should be able to pass a value as the last element or the URL" do
|
30
|
+
response = @request.get("/home/events/event4/quo%20vadis")
|
31
|
+
response.body.should == "the value is quo vadis"
|
32
|
+
end
|
28
33
|
|
29
34
|
it "should invoke the before_load method if provided by the page" do
|
30
35
|
response = @request.get("/before_load")
|
@@ -54,3 +59,35 @@ describe Trellis::Page, " when created with a custom route" do
|
|
54
59
|
end
|
55
60
|
end
|
56
61
|
|
62
|
+
describe Trellis::Page, " when given a template" do
|
63
|
+
before(:each) do
|
64
|
+
@application = TestApp::MyApp.new
|
65
|
+
@request = Rack::MockRequest.new(@application)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should rendered it correctly if it is in HAML format" do
|
69
|
+
response = @request.get("/haml_page")
|
70
|
+
response.body.should == "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n <head>\n <meta content=\"text/html; charset=UTF-8\" http-equiv=\"Content-Type\" />\n <title>\n This is a HAML page\n </title>\n </head>\n <body>\n <h1>\n Page Title\n </h1>\n <p>\n HAML rocks!\n </p>\n </body>\n</html>\n"
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should rendered it correctly if it is in Textile format" do
|
74
|
+
response = @request.get("/textile_page")
|
75
|
+
response.body.should == "<p>A <strong>simple</strong> example.</p>"
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should rendered it correctly if it is in Markdown format" do
|
79
|
+
response = @request.get("/mark_down_page")
|
80
|
+
response.body.should == "<html><body><h1>This is the Title</h1>\n\n<h2>This is the SubTitle</h2>\n\n<p>This is some text</p></body></html>"
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should rendered it correctly if it is in ERuby format" do
|
84
|
+
response = @request.get("/e_ruby_page")
|
85
|
+
response.body.should == "<html><body><ul><li>Hey</li><li>bud</li><li>let's</li><li>party!</li></ul></body></html>"
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should rendered it correctly if it is in HTML format" do
|
89
|
+
response = @request.get("/html_page")
|
90
|
+
response.body.should == "<html><body><h1>This is just HTML</h1></body></html>"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
data/test/renderer_spec.rb
CHANGED
@@ -1,12 +1,31 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
2
|
|
3
|
+
require "rack"
|
4
|
+
require_fixtures 'application_spec_applications'
|
5
|
+
|
3
6
|
describe Trellis::Renderer do
|
4
|
-
|
5
|
-
|
7
|
+
|
8
|
+
it "should render a given page template" do
|
9
|
+
page = TestApp::Home.new
|
10
|
+
renderer = Trellis::Renderer.new(page)
|
11
|
+
result = renderer.render
|
12
|
+
result.should == "<html><body><h1>Hello World!</h1></body></html>"
|
6
13
|
end
|
7
14
|
|
8
|
-
it "should
|
9
|
-
|
15
|
+
it "should have access to page instance variables" do
|
16
|
+
page = TestApp::SamplePage.new
|
17
|
+
page.value = "chunky bacon"
|
18
|
+
renderer = Trellis::Renderer.new(page)
|
19
|
+
result = renderer.render
|
20
|
+
result.should == "<html><body>chunky bacon</body></html>"
|
10
21
|
end
|
22
|
+
|
23
|
+
it "should have access to the page name" do
|
24
|
+
page = TestApp::AnotherSamplePage.new
|
25
|
+
renderer = Trellis::Renderer.new(page)
|
26
|
+
result = renderer.render
|
27
|
+
result.should == "<html><body>TestApp::AnotherSamplePage</body></html>"
|
28
|
+
end
|
29
|
+
|
11
30
|
end
|
12
31
|
|
data/test/router_spec.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trellis
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Sam-Bodden
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-11-08 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -30,7 +30,7 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.
|
33
|
+
version: 1.0.1
|
34
34
|
version:
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
36
|
name: radius
|
@@ -40,7 +40,7 @@ dependencies:
|
|
40
40
|
requirements:
|
41
41
|
- - ">="
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
version: 0.
|
43
|
+
version: 0.6.1
|
44
44
|
version:
|
45
45
|
- !ruby/object:Gem::Dependency
|
46
46
|
name: builder
|
@@ -80,7 +80,7 @@ dependencies:
|
|
80
80
|
requirements:
|
81
81
|
- - ">="
|
82
82
|
- !ruby/object:Gem::Version
|
83
|
-
version: 2.
|
83
|
+
version: 2.2.9
|
84
84
|
version:
|
85
85
|
- !ruby/object:Gem::Dependency
|
86
86
|
name: RedCloth
|
@@ -90,17 +90,17 @@ dependencies:
|
|
90
90
|
requirements:
|
91
91
|
- - ">="
|
92
92
|
- !ruby/object:Gem::Version
|
93
|
-
version: 4.
|
93
|
+
version: 4.2.2
|
94
94
|
version:
|
95
95
|
- !ruby/object:Gem::Dependency
|
96
|
-
name:
|
96
|
+
name: bluecloth
|
97
97
|
type: :runtime
|
98
98
|
version_requirement:
|
99
99
|
version_requirements: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - ">="
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version:
|
103
|
+
version: 2.0.5
|
104
104
|
version:
|
105
105
|
- !ruby/object:Gem::Dependency
|
106
106
|
name: markaby
|
@@ -120,47 +120,87 @@ dependencies:
|
|
120
120
|
requirements:
|
121
121
|
- - ">="
|
122
122
|
- !ruby/object:Gem::Version
|
123
|
-
version: 1.
|
123
|
+
version: 1.1.2
|
124
124
|
version:
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
126
|
+
name: facets
|
127
127
|
type: :runtime
|
128
128
|
version_requirement:
|
129
129
|
version_requirements: !ruby/object:Gem::Requirement
|
130
130
|
requirements:
|
131
131
|
- - ">="
|
132
132
|
- !ruby/object:Gem::Version
|
133
|
-
version:
|
133
|
+
version: 2.7.0
|
134
134
|
version:
|
135
135
|
- !ruby/object:Gem::Dependency
|
136
|
-
name:
|
136
|
+
name: directory_watcher
|
137
137
|
type: :runtime
|
138
138
|
version_requirement:
|
139
139
|
version_requirements: !ruby/object:Gem::Requirement
|
140
140
|
requirements:
|
141
141
|
- - ">="
|
142
142
|
- !ruby/object:Gem::Version
|
143
|
-
version:
|
143
|
+
version: 1.2.0
|
144
144
|
version:
|
145
145
|
- !ruby/object:Gem::Dependency
|
146
|
-
name:
|
146
|
+
name: rack-cache
|
147
147
|
type: :runtime
|
148
148
|
version_requirement:
|
149
149
|
version_requirements: !ruby/object:Gem::Requirement
|
150
150
|
requirements:
|
151
151
|
- - ">="
|
152
152
|
- !ruby/object:Gem::Version
|
153
|
-
version: 0.
|
153
|
+
version: 0.5.2
|
154
154
|
version:
|
155
155
|
- !ruby/object:Gem::Dependency
|
156
|
-
name:
|
156
|
+
name: rack-contrib
|
157
157
|
type: :runtime
|
158
158
|
version_requirement:
|
159
159
|
version_requirements: !ruby/object:Gem::Requirement
|
160
160
|
requirements:
|
161
161
|
- - ">="
|
162
162
|
- !ruby/object:Gem::Version
|
163
|
-
version: 0.2
|
163
|
+
version: 0.9.2
|
164
|
+
version:
|
165
|
+
- !ruby/object:Gem::Dependency
|
166
|
+
name: rack-test
|
167
|
+
type: :runtime
|
168
|
+
version_requirement:
|
169
|
+
version_requirements: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: 0.5.0
|
174
|
+
version:
|
175
|
+
- !ruby/object:Gem::Dependency
|
176
|
+
name: erubis
|
177
|
+
type: :runtime
|
178
|
+
version_requirement:
|
179
|
+
version_requirements: !ruby/object:Gem::Requirement
|
180
|
+
requirements:
|
181
|
+
- - ">="
|
182
|
+
- !ruby/object:Gem::Version
|
183
|
+
version: 2.6.5
|
184
|
+
version:
|
185
|
+
- !ruby/object:Gem::Dependency
|
186
|
+
name: rspec
|
187
|
+
type: :runtime
|
188
|
+
version_requirement:
|
189
|
+
version_requirements: !ruby/object:Gem::Requirement
|
190
|
+
requirements:
|
191
|
+
- - ">="
|
192
|
+
- !ruby/object:Gem::Version
|
193
|
+
version: 1.2.9
|
194
|
+
version:
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: newgem
|
197
|
+
type: :runtime
|
198
|
+
version_requirement:
|
199
|
+
version_requirements: !ruby/object:Gem::Requirement
|
200
|
+
requirements:
|
201
|
+
- - ">="
|
202
|
+
- !ruby/object:Gem::Version
|
203
|
+
version: 1.5.2
|
164
204
|
version:
|
165
205
|
- !ruby/object:Gem::Dependency
|
166
206
|
name: hoe
|
@@ -183,13 +223,16 @@ extra_rdoc_files:
|
|
183
223
|
- History.txt
|
184
224
|
- License.txt
|
185
225
|
- Manifest.txt
|
226
|
+
- README.txt
|
186
227
|
- examples/examples.txt
|
187
228
|
- examples/hangman/html/resources/word_list.txt
|
188
229
|
files:
|
230
|
+
- .gitignore
|
189
231
|
- History.txt
|
190
232
|
- License.txt
|
191
233
|
- Manifest.txt
|
192
234
|
- README
|
235
|
+
- README.txt
|
193
236
|
- Rakefile
|
194
237
|
- config/hoe.rb
|
195
238
|
- config/requirements.rb
|
@@ -297,6 +340,7 @@ files:
|
|
297
340
|
- test/core_extensions_spec.rb
|
298
341
|
- test/default_router_spec.rb
|
299
342
|
- test/fixtures/application_spec_applications.rb
|
343
|
+
- test/fixtures/component_spec_components.rb
|
300
344
|
- test/page_spec.rb
|
301
345
|
- test/renderer_spec.rb
|
302
346
|
- test/router_spec.rb
|