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 ADDED
@@ -0,0 +1,5 @@
1
+ nbproject
2
+ .DS_Store
3
+ Thumbs.db
4
+ **/.svn
5
+ website
data/History.txt CHANGED
@@ -1,6 +1,23 @@
1
- == 0.0.2 2009-09-19
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
- * 3 major enhancements:
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
- * Initial release
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
@@ -2,3 +2,6 @@ require 'config/requirements'
2
2
  require 'config/hoe' # setup Hoe + all gem configuration
3
3
 
4
4
  Dir['tasks/**/*.rake'].each { |rake| load rake }
5
+
6
+ task :default => :spec
7
+
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.4.0'],
13
- ['radius', '>= 0.5.1'],
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.0.3'],
18
- ['RedCloth', '>= 4.0.3'],
19
- ['BlueCloth', '>= 1.0.0'],
17
+ ['haml', '>= 2.2.9'],
18
+ ['RedCloth', '>= 4.2.2'],
19
+ ['bluecloth', '>= 2.0.5'],
20
20
  ['markaby', '>= 0.5'],
21
- ['log4r', '>= 1.0.5'],
22
- ['dm-core', '>= 0.9.5'],
23
- ['dm-validations', '>= 0.9.5'],
24
- ['dm-timestamps', '>= 0.9.5'],
25
- ['english', '>= 0.2.0']
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.new(GEM_NAME, VERS) do |p|
66
- p.developer(AUTHOR, EMAIL)
67
- p.description = DESCRIPTION
68
- p.summary = DESCRIPTION
69
- p.url = HOMEPATH
70
- p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
71
- p.test_globs = ["test/**/test_*.rb"]
72
- p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
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
- p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
76
- p.extra_deps = EXTRA_DEPENDENCIES
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")
@@ -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["rack.session"]
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
- @parser.parse(@page.class.parsed_template.to_html)
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
@@ -28,7 +28,7 @@ module Trellis
28
28
  module VERSION #:nodoc:
29
29
  MAJOR = 0
30
30
  MINOR = 0
31
- TINY = 4
31
+ TINY = 5
32
32
 
33
33
  STRING = [MAJOR, MINOR, TINY].join('.')
34
34
  end
@@ -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 splat" do
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 " do
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
+
@@ -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
- before(:each) do
5
- #@renderer = Renderer.new
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 desc" do
9
- # TODO
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
@@ -48,5 +48,5 @@ describe Trellis::Router, " when constructed" do
48
48
  @router = Trellis::Router.new(:path => '/*')
49
49
  @router.keys.should include('splat')
50
50
  end
51
-
51
+
52
52
  end
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
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-10-06 00:00:00 -07:00
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.4.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.5.1
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.0.3
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.0.3
93
+ version: 4.2.2
94
94
  version:
95
95
  - !ruby/object:Gem::Dependency
96
- name: BlueCloth
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: 1.0.0
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.0.5
123
+ version: 1.1.2
124
124
  version:
125
125
  - !ruby/object:Gem::Dependency
126
- name: dm-core
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: 0.9.5
133
+ version: 2.7.0
134
134
  version:
135
135
  - !ruby/object:Gem::Dependency
136
- name: dm-validations
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: 0.9.5
143
+ version: 1.2.0
144
144
  version:
145
145
  - !ruby/object:Gem::Dependency
146
- name: dm-timestamps
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.9.5
153
+ version: 0.5.2
154
154
  version:
155
155
  - !ruby/object:Gem::Dependency
156
- name: english
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.0
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