trellis 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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