trellis 0.0.4 → 0.0.5
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.
- 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
|