trellis 0.1.0 → 0.1.1

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.
@@ -1,6 +1,12 @@
1
+ == 0.1.1
2
+ * 1 major enhancement
3
+ * before, around and after filters
4
+ * refactoring (clean up) of call! (rack specification) method
5
+
1
6
  == 0.1.0
2
7
  * 1 major enhancement
3
8
  * fix logger for rack 1.0.1
9
+
4
10
  == 0.0.9
5
11
  * 1 major enhancement:
6
12
  * added missing nokogiri gem from the dependencies
@@ -1,4 +1,3 @@
1
- .gitignore
2
1
  History.txt
3
2
  License.txt
4
3
  Manifest.txt
@@ -19,6 +18,7 @@ examples/crud_components/html/style/trellis.css
19
18
  examples/crud_components/source/crud_components.rb
20
19
  examples/crud_components/source/domain.rb
21
20
  examples/examples.txt
21
+ examples/filters/source/filters.rb
22
22
  examples/flickr/source/flickr.rb
23
23
  examples/guest_book/source/guest_book.rb
24
24
  examples/hangman/html/game_over.xhtml
@@ -111,8 +111,10 @@ test/application_spec.rb
111
111
  test/component_spec.rb
112
112
  test/core_extensions_spec.rb
113
113
  test/default_router_spec.rb
114
- test/fixtures/application_spec_applications.rb
115
- test/fixtures/component_spec_components.rb
114
+ test/filters_spec.rb
115
+ test/fixtures/application_fixtures.rb
116
+ test/fixtures/component_fixtures.rb
117
+ test/fixtures/filters_fixtures.rb
116
118
  test/page_spec.rb
117
119
  test/renderer_spec.rb
118
120
  test/router_spec.rb
@@ -27,7 +27,8 @@ EXTRA_DEPENDENCIES = [
27
27
  ['rack-test', '>= 0.5.3'],
28
28
  ['erubis', '>= 2.6.5'],
29
29
  ['rspec', '>= 1.2.9'],
30
- ['newgem', '>= 1.5.2']
30
+ ['newgem', '>= 1.5.2'],
31
+ ['advisable', '>= 1.0.0']
31
32
  ] # An array of rubygem dependencies [name, version]
32
33
 
33
34
  @config_file = "~/.rubyforge/user-config.yml"
@@ -0,0 +1,141 @@
1
+ require 'rubygems'
2
+ require 'trellis'
3
+
4
+ include Trellis
5
+
6
+ module Filters
7
+
8
+ class Filters < Application
9
+ home :login
10
+ persistent :user
11
+
12
+ logger.level = DEBUG
13
+
14
+ # hardcoded user name and password (yeah don't do this)
15
+ USER_NAME = "admin"
16
+ PASSWORD = "itsasecret!"
17
+
18
+ # helper methods
19
+ def admin?
20
+ @user
21
+ end
22
+
23
+ filter :authorized?, :around do |page, &block|
24
+ page.application.admin? ? block.call : page.redirect("/not_authorized")
25
+ end
26
+
27
+ filter :capitalize, :after do |page|
28
+ page.answer = "#{page.answer} is #{page.answer.reverse} backwards"
29
+ end
30
+
31
+ def user
32
+ @user = false unless @user
33
+ @user
34
+ end
35
+
36
+ layout :main, %[
37
+ <html xml:lang="en" lang="en" xmlns:trellis="http://trellisframework.org/schema/trellis_1_0_0.xsd" xmlns="http://www.w3.org/1999/xhtml">
38
+ <head>
39
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
40
+ <title>@{@page_name}@</title>
41
+ </head>
42
+ <body>
43
+ <h1>Trellis Filters Demo</h1>
44
+ <?rb if admin? ?>
45
+ <h2>Logged in as @{@application.user}@</h2>
46
+ <?rb end ?>
47
+ <ul>
48
+ <li><a href="/protected">Protected Simple Page</a></li>
49
+ <li><a href="/protected_with_get">Protected Page with get method</a></li>
50
+ <li><a href="/protected_event/events/knock_knock">Protected Page Trigger by event</a></li>
51
+ <?rb if admin? ?>
52
+ <li><a href="/login/events/logout">Logout</a></li>
53
+ <?rb else ?>
54
+ <li><a href="/login">Login</a></li>
55
+ <?rb end ?>
56
+ </ul>
57
+ @!{@body}@
58
+ </body>
59
+ </html>
60
+ ], :format => :eruby
61
+ end
62
+
63
+ class Login < Page
64
+ route '/login'
65
+
66
+ def on_submit_from_login
67
+ if params[:login_name] == Filters::USER_NAME && params[:login_password] == Filters::PASSWORD
68
+ @application.user = Filters::USER_NAME
69
+ redirect "/protected"
70
+ else
71
+ self
72
+ end
73
+ end
74
+
75
+ def on_logout
76
+ @application.user = false
77
+ self
78
+ end
79
+
80
+ template %[
81
+ <trellis:form tid="login" method="post" class="camisa-login">
82
+ <label for="name">Username:</label>
83
+ <trellis:text_field tid="name" id="name" />
84
+ <label for="password">Password:</label>
85
+ <trellis:password tid="password" id="password" />
86
+ <trellis:submit tid="add" value="Login">
87
+ </trellis:form>
88
+ ], :format => :html, :layout => :main
89
+ end
90
+
91
+ class Protected < Page
92
+ apply_filter :authorized?, :to => :all
93
+
94
+ template %[
95
+ <h1>This page is protected by an around filter</h1>
96
+ <h2>Shhhhhh!</h2>
97
+ ], :format => :html, :layout => :main
98
+ end
99
+
100
+ class ProtectedWithGet < Page
101
+ apply_filter :authorized?, :to => :all
102
+
103
+ def get
104
+ self
105
+ end
106
+
107
+ template %[
108
+ <h1>This page is also protected by an around filter</h1>
109
+ <h2>Let's also keep it quiet!</h2>
110
+ ], :format => :html, :layout => :main
111
+ end
112
+
113
+ class ProtectedEvent < Page
114
+ persistent :answer
115
+ apply_filter :authorized?, :to => :on_knock_knock
116
+ apply_filter :capitalize, :to => :on_knock_knock
117
+
118
+ def initialize
119
+ @answer = "blah"
120
+ end
121
+
122
+ def on_knock_knock
123
+ @answer = "who's there?"
124
+ self
125
+ end
126
+
127
+ template %[
128
+ <h1>Only a few chosen can see this</h1>
129
+ <h2>@{@answer}@</h2>
130
+ ], :format => :eruby, :layout => :main
131
+ end
132
+
133
+ class NotAuthorized < Page
134
+ template %[
135
+ <h1>You are not authorized to see the page</h1>
136
+ <h2>Move along stranger!</h2>
137
+ ], :format => :html, :layout => :main
138
+ end
139
+
140
+ Filters.new.start if __FILE__ == $PROGRAM_NAME
141
+ end
@@ -3,7 +3,7 @@
3
3
  #--
4
4
  # Copyright &169;2001-2008 Integrallis Software, LLC.
5
5
  # All Rights Reserved.
6
- #
6
+ # process
7
7
  # Permission is hereby granted, free of charge, to any person obtaining
8
8
  # a copy of this software and associated documentation files (the
9
9
  # "Software"), to deal in the Software without restriction, including
@@ -40,6 +40,7 @@ require 'facets'
40
40
  require 'directory_watcher'
41
41
  require 'erubis'
42
42
  require 'ostruct'
43
+ require 'advisable'
43
44
 
44
45
  module Trellis
45
46
 
@@ -55,6 +56,7 @@ module Trellis
55
56
 
56
57
  @@partials = Hash.new
57
58
  @@layouts = Hash.new
59
+ @@filters = Hash.new
58
60
 
59
61
  # descendant application classes get a singleton class level instances for
60
62
  # holding homepage, dependent pages, static resource routing paths
@@ -106,6 +108,15 @@ module Trellis
106
108
  store_template(name, :layout, body, options, &block)
107
109
  end
108
110
 
111
+ def self.filters
112
+ @@filters
113
+ end
114
+
115
+ def self.filter(name, kind = :before, &block)
116
+ name = name.to_sym unless name.class == Symbol
117
+ @@filters[name] = OpenStruct.new({:name => name, :kind => kind, :block => block})
118
+ end
119
+
109
120
  # bootstrap the application
110
121
  def start(port = 3000)
111
122
  Application.logger.info "Starting Trellis Application #{self.class} on port #{port}"
@@ -173,7 +184,7 @@ module Trellis
173
184
  response = Rack::Response.new
174
185
  request = Rack::Request.new(env)
175
186
 
176
- Application.logger.debug "request received with url_root of #{request.script_name}" if request.script_name
187
+ Application.logger.debug "request received with url_root of #{request.script_name}" unless request.script_name.blank?
177
188
 
178
189
  session = env['rack.session'] ||= {}
179
190
 
@@ -184,7 +195,6 @@ module Trellis
184
195
  if page
185
196
  load_persistent_fields_data(session)
186
197
  page.application = self
187
-
188
198
  page.class.url_root = request.script_name
189
199
  page.path = request.path_info.sub(/^\//, '')
190
200
  page.inject_dependent_pages
@@ -193,47 +203,56 @@ module Trellis
193
203
  page.call_if_provided(:after_load)
194
204
  page.params = request.params.keys_to_symbols
195
205
  router.inject_parameters_into_page_instance(page, request)
196
- result = route.event ? page.process_event(route.event, route.value, route.source, session) : page
197
206
 
207
+ result = route.event ? page.process_event(route.event, route.value, route.source, session) : page
208
+
198
209
  Application.logger.debug "response is #{result} an instance of #{result.class}"
199
210
 
200
211
  # -------------------------
201
212
  # prepare the http response
202
213
  # -------------------------
203
214
 
204
- if result.kind_of?(Trellis::Redirect)
205
- # redirect short circuits
206
- result.apply_to(request, response)
207
- Application.logger.debug "redirecting to ==> #{request.script_name}/#{result.target}"
208
- elsif (request.post? || route.event) && result.kind_of?(Trellis::Page)
209
- # for action events of posts then use redirect after post pattern
210
- # remove the events path and just return to the page
211
- path = result.path ? result.path.gsub(/\/events\/.*/, '') : result.class.class_to_sym
212
- response.status = 302
213
- response.headers["Location"] = "#{request.script_name}/#{path}"
214
- Application.logger.debug "redirecting to ==> #{request.script_name}/#{path}"
215
- else
216
- # handle the get method
217
- if result.kind_of?(Trellis::Page) && result.respond_to?(:get)
218
- get = result.get
219
- if get.kind_of?(Trellis::Redirect)
220
- # redirect short circuits
221
- get.apply_to(request, response)
222
- Application.logger.debug "redirecting to ==> #{request.script_name}/#{get.target}"
223
- elsif (get.class == result.class) || !get.kind_of?(Trellis::Page)
224
- response.body = get.kind_of?(Trellis::Page) ? get.render : get
225
- response.status = 200
226
- else
227
- path = get.path ? get.path.gsub(/\/events\/.*/, '') : get.class.class_to_sym
228
- response.status = 302
229
- response.headers["Location"] = "#{request.script_name}/#{path}"
230
- Application.logger.debug "redirecting to ==> #{request.script_name}/#{path}"
231
- end
215
+ # -------------------------------------
216
+ # process the 'get' method if available
217
+ # -------------------------------------
218
+ same_class = true
219
+ if result.kind_of?(Trellis::Page) && result.respond_to?(:get)
220
+ result_cls = result.class
221
+ result = result.get
222
+ same_class = result.class == result_cls
223
+ Application.logger.debug "processed get method, result.class is now => #{result.class}"
224
+ end
225
+
226
+ case result
227
+ # -----------------
228
+ # explicit redirect
229
+ # -----------------
230
+ when Trellis::Redirect
231
+ result.process(request, response)
232
+ Application.logger.debug "redirecting (explicit) to ==> #{request.script_name}/#{result.target}"
233
+ # -----------------
234
+ # implicit redirect
235
+ # -----------------
236
+ when Trellis::Page
237
+ # redirect after POST or 'get' method returns a different page
238
+ if (request.post? || route.event) || !same_class
239
+ path = result.path ? result.path.gsub(/\/events\/.*/, '') : result.class.class_to_sym
240
+ response.status = 302
241
+ response.headers["Location"] = "#{request.script_name}/#{path}"
242
+ Application.logger.debug "redirecting (implicit) to ==> #{request.script_name}/#{path}"
243
+ # simply render page
232
244
  else
233
- # for render requests simply render the page
234
- response.body = result.kind_of?(Trellis::Page) ? result.render : result
245
+ response.body = result.render
235
246
  response.status = 200
247
+ Application.logger.debug "rendering page #{result}"
236
248
  end
249
+ # -------------------------------
250
+ # stringify any other result type
251
+ # -------------------------------
252
+ else
253
+ response.body = result.to_s
254
+ response.status = 200
255
+ Application.logger.debug "rendering #{result}"
237
256
  end
238
257
  else
239
258
  response.status = 404
@@ -491,7 +510,7 @@ module Trellis
491
510
  @target, @status = target, status
492
511
  end
493
512
 
494
- def apply_to(request, response)
513
+ def process(request, response)
495
514
  response.status = status
496
515
  response["Location"] = "#{request.script_name}#{target.starts_with?('/') ? '' : '/'}#{target}"
497
516
  end
@@ -504,6 +523,7 @@ module Trellis
504
523
  # A Trellis Page contains listener methods to respond to events trigger by
505
524
  # components in the same page or other pages
506
525
  class Page
526
+ extend Advisable
507
527
  include Nokogiri::XML
508
528
 
509
529
  @@subclasses = Hash.new
@@ -598,6 +618,27 @@ module Trellis
598
618
  @persistents = @persistents | fields
599
619
  end
600
620
 
621
+ def self.apply_filter(name, options = {})
622
+ filter = Application.filters[name]
623
+ methods = options[:to] == :all ? self.public_instance_methods(false) : [options[:to]]
624
+ methods << :get if options[:to] == :all
625
+ methods = methods - [:before_load, :after_load, :before_render, :after_render, :underscore_class_name]
626
+ Application.logger.debug "in #{self} applying filter #{name} to methods: #{methods.join(', ')}"
627
+
628
+ methods.each do |method|
629
+ case filter.kind
630
+ when :around
631
+ around method do |target|
632
+ filter.block.call(self) { target.call }
633
+ end
634
+ when :before
635
+ before method do filter.block.call(self) end
636
+ when :after
637
+ after method do filter.block.call(self) end
638
+ end
639
+ end
640
+ end
641
+
601
642
  def self.get_page(sym)
602
643
  @@subclasses[sym]
603
644
  end
@@ -613,6 +654,8 @@ module Trellis
613
654
  @logger = Application.logger
614
655
  end
615
656
 
657
+ def get; self; end
658
+
616
659
  def redirect(path, status=nil)
617
660
  Redirect.new(path, status)
618
661
  end
@@ -28,7 +28,7 @@ module Trellis
28
28
  module VERSION #:nodoc:
29
29
  MAJOR = 0
30
30
  MINOR = 1
31
- TINY = 0
31
+ TINY = 1
32
32
 
33
33
  STRING = [MAJOR, MINOR, TINY].join('.')
34
34
  end
@@ -18,5 +18,5 @@ desc "Run the specs under spec"
18
18
  Spec::Rake::SpecTask.new do |t|
19
19
  t.spec_opts = ['--options', "test/spec.opts"]
20
20
  t.spec_files = FileList['test/**/*_spec.rb']
21
- #t.rcov = true
21
+ t.rcov = true
22
22
  end
@@ -2,7 +2,7 @@ require File.dirname(__FILE__) + '/spec_helper.rb'
2
2
 
3
3
  require "rack"
4
4
  require 'rack/test'
5
- require_fixtures 'application_spec_applications'
5
+ require_fixtures 'application_fixtures'
6
6
 
7
7
  describe Trellis::Application, " when declared" do
8
8
  before do
@@ -2,7 +2,7 @@ require File.dirname(__FILE__) + '/spec_helper.rb'
2
2
 
3
3
  require "rack"
4
4
  require 'rack/test'
5
- require_fixtures 'component_spec_components'
5
+ require_fixtures 'component_fixtures'
6
6
 
7
7
  describe Trellis::Component, " in an application" do
8
8
  include Rack::Test::Methods
@@ -0,0 +1,61 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ require "rack"
4
+ require "rack/test"
5
+ require_fixtures 'filters_fixtures'
6
+
7
+ describe Trellis::Page, " with applied filters " do
8
+ include Rack::Test::Methods
9
+
10
+ before do
11
+ @app = FiltersApp::FiltersApp.new
12
+ end
13
+
14
+ def app
15
+ @app
16
+ end
17
+
18
+ it "should be redirected by an around filter to a specific destination" do
19
+ get "/protected_one"
20
+ redirect = last_response.headers['Location']
21
+ redirect.should == '/not_authorized'
22
+ end
23
+
24
+ it "should be allowed to render by an around filter" do
25
+ @app.allow = true
26
+ get "/protected_one"
27
+ last_response.body.should == %[<?xml version=\"1.0\"?>\n<p>protected one</p>\n]
28
+ end
29
+
30
+ it "should be redirected by an around filter to a specific destination when using the get method" do
31
+ get "/protected_two"
32
+ redirect = last_response.headers['Location']
33
+ redirect.should == '/not_authorized'
34
+ end
35
+
36
+ it "should be allowed to process the get method by an around filter" do
37
+ @app.allow = true
38
+ get "/protected_two"
39
+ last_response.body.should == %[<?xml version=\"1.0\"?>\n<p>protected two</p>\n]
40
+ end
41
+
42
+ it "should apply filters only to the specified methods" do
43
+ get "/protected_three"
44
+ last_response.body.should == %[<?xml version=\"1.0\"?>\n<p>blah</p>\n]
45
+ get "/protected_three/events/knock_knock"
46
+ redirect = last_response.headers['Location']
47
+ redirect.should == '/not_authorized'
48
+ end
49
+
50
+ it "should allow to daisy chain filters" do
51
+ @app.allow = true
52
+ env = Hash.new
53
+ env["rack.session"] = Hash.new
54
+ get "/protected_three/events/knock_knock", {}, env
55
+ redirect = last_response.headers['Location']
56
+ redirect.should eql('/protected_three')
57
+ get redirect, {}, env
58
+ last_response.body.should include("<?xml version=\"1.0\"?>\n<p>?ereht s'ohw</p>\n")
59
+ end
60
+
61
+ end
@@ -0,0 +1,44 @@
1
+ module FiltersApp
2
+ class FiltersApp < Trellis::Application
3
+ attr_accessor :allow
4
+ home :protected_one
5
+
6
+ filter :authorized?, :around do |page, &block|
7
+ page.application.allow ? block.call : page.redirect("/not_authorized")
8
+ end
9
+
10
+ filter :capitalize, :after do |page|
11
+ page.answer = page.answer.reverse
12
+ end
13
+ end
14
+
15
+ class ProtectedOne < Trellis::Page
16
+ apply_filter :authorized?, :to => :all
17
+ template %[<p>protected one</p>], :format => :html
18
+ end
19
+
20
+ class ProtectedTwo < Trellis::Page
21
+ apply_filter :authorized?, :to => :all
22
+ def get; self; end
23
+ template %[<p>protected two</p>], :format => :html
24
+ end
25
+
26
+ class ProtectedThree < Trellis::Page
27
+ persistent :answer
28
+ apply_filter :authorized?, :to => :on_knock_knock
29
+ apply_filter :capitalize, :to => :on_knock_knock
30
+
31
+ def initialize; @answer = "blah"; end
32
+
33
+ def on_knock_knock
34
+ @answer = "who's there?"
35
+ self
36
+ end
37
+
38
+ template %[<p>@{@answer}@</p>], :format => :eruby
39
+ end
40
+
41
+ class NotAuthorized < Trellis::Page
42
+ template %[not authorized], :format => :html
43
+ end
44
+ end
@@ -2,7 +2,7 @@ require File.dirname(__FILE__) + '/spec_helper.rb'
2
2
 
3
3
  require "rack"
4
4
  require "rack/test"
5
- require_fixtures 'application_spec_applications'
5
+ require_fixtures 'application_fixtures'
6
6
 
7
7
  describe Trellis::Page, " when sending an event to a page" do
8
8
  include Rack::Test::Methods
@@ -1,7 +1,7 @@
1
1
  require File.dirname(__FILE__) + '/spec_helper.rb'
2
2
 
3
3
  require "rack"
4
- require_fixtures 'application_spec_applications'
4
+ require_fixtures 'application_fixtures'
5
5
 
6
6
  describe Trellis::Renderer do
7
7
 
@@ -1,6 +1,6 @@
1
1
  require File.dirname(__FILE__) + '/spec_helper.rb'
2
2
 
3
- require_fixtures 'application_spec_applications'
3
+ require_fixtures 'application_fixtures'
4
4
 
5
5
  describe Trellis::Router, " when constructed" do
6
6
 
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.1.0
4
+ version: 0.1.1
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: 2010-01-12 00:00:00 -07:00
12
+ date: 2010-01-24 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -212,6 +212,16 @@ dependencies:
212
212
  - !ruby/object:Gem::Version
213
213
  version: 1.5.2
214
214
  version:
215
+ - !ruby/object:Gem::Dependency
216
+ name: advisable
217
+ type: :runtime
218
+ version_requirement:
219
+ version_requirements: !ruby/object:Gem::Requirement
220
+ requirements:
221
+ - - ">="
222
+ - !ruby/object:Gem::Version
223
+ version: 1.0.0
224
+ version:
215
225
  - !ruby/object:Gem::Dependency
216
226
  name: rubyforge
217
227
  type: :development
@@ -257,7 +267,6 @@ extra_rdoc_files:
257
267
  - examples/examples.txt
258
268
  - examples/hangman/html/resources/word_list.txt
259
269
  files:
260
- - .gitignore
261
270
  - History.txt
262
271
  - License.txt
263
272
  - Manifest.txt
@@ -278,6 +287,7 @@ files:
278
287
  - examples/crud_components/source/crud_components.rb
279
288
  - examples/crud_components/source/domain.rb
280
289
  - examples/examples.txt
290
+ - examples/filters/source/filters.rb
281
291
  - examples/flickr/source/flickr.rb
282
292
  - examples/guest_book/source/guest_book.rb
283
293
  - examples/hangman/html/game_over.xhtml
@@ -370,8 +380,10 @@ files:
370
380
  - test/component_spec.rb
371
381
  - test/core_extensions_spec.rb
372
382
  - test/default_router_spec.rb
373
- - test/fixtures/application_spec_applications.rb
374
- - test/fixtures/component_spec_components.rb
383
+ - test/filters_spec.rb
384
+ - test/fixtures/application_fixtures.rb
385
+ - test/fixtures/component_fixtures.rb
386
+ - test/fixtures/filters_fixtures.rb
375
387
  - test/page_spec.rb
376
388
  - test/renderer_spec.rb
377
389
  - test/router_spec.rb
data/.gitignore DELETED
@@ -1,5 +0,0 @@
1
- nbproject
2
- .DS_Store
3
- Thumbs.db
4
- **/.svn
5
- website