togo 0.6.2 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,12 +1,17 @@
1
1
  # Introducing Togo
2
2
 
3
- Togo is a CMS framework. It also includes a simple web framework based on Rack that can be used to make micro-webapps (similar to Sinatra).
4
- However the goal of Togo is to be a modular system that can be used with any Ruby ORM framework, giving you a complete CMS system for free.
3
+ Togo is a CMS framework. The goal of Togo is to be a modular system that can be used with any Ruby ORM framework, giving you a complete CMS right out of the box.
4
+ It is built to be as independent as possible from other web frameworks such as Rails or Sinatra, and only needs Rack and your choice of HTTP server to run (such as thin, mongrel or webrick).
5
+ Togo is focused only on making a quick and easy way to manage content, and is less about integrating into your existing web application.
5
6
 
6
7
  ## Installation
7
8
 
8
9
  gem install togo
9
10
 
11
+ ## Example Application
12
+
13
+ An example application is available: [http://github.com/mattking17/Togo-Example-App](http://github.com/mattking17/Togo-Example-App)
14
+
10
15
  ## Tutorial
11
16
 
12
17
  Togo works by a simply including a line in your model definition (currently, Togo only works for DataMapper). By including Togo in your model,
@@ -20,7 +25,7 @@ gives a good idea of how Togo can be used in conjuction with an existing project
20
25
 
21
26
  You'll need the following gems installed:
22
27
 
23
- pre. dm-core dm-serializer sinatra dm-sqlite3-adapter
28
+ dm-core dm-serializer sinatra dm-sqlite3-adapter
24
29
 
25
30
  Next, make a directory for your project like so:
26
31
 
@@ -105,8 +110,8 @@ Open up http://0.0.0.0:8080 in your browser and have a blast! Have a look at our
105
110
 
106
111
  ## Customizing Togo
107
112
 
108
- Now you have a complete CMS system for free, but you can also customize what fields are used, what order they appear, and even
109
- use your own templates for complete customization.
113
+ Togo is set up to work with sensible defaults out of the box, but you can customize what fields are used, what order they appear, and even
114
+ use your own templates to show fields when editing or creating content.
110
115
 
111
116
  ### Telling Togo which fields to use
112
117
 
@@ -131,7 +136,7 @@ If you only want the title and date field to appear in the list view of blog ent
131
136
 
132
137
  Togo will display the fields in the order given. So if you wanted date first some reason, you could just do
133
138
 
134
- pre. list_properties :date, :title
139
+ list_properties :date, :title
135
140
 
136
141
  And date will be listed in the first column.
137
142
 
@@ -166,6 +171,24 @@ Associations also can be used in list and form property declarations:
166
171
 
167
172
  end
168
173
 
174
+ Togo also allows for displaying values returned from instance methods on your model.
175
+ This opens up customization of the list display even further.
176
+
177
+ class BlogEntry
178
+
179
+ include DataMapper::Resource
180
+ include Togo::DataMapper::Model
181
+ ... snipped ...
182
+
183
+ list_properties :title, :date, :number_of_comments
184
+ form_properties :title, :date, :body, :comments
185
+
186
+ def number_of_comments
187
+ comments.count
188
+ end
189
+
190
+ end
191
+
169
192
 
170
193
  #### Configuring properties
171
194
 
@@ -207,4 +230,82 @@ Each property type has it's own default form template that can be overridden sim
207
230
 
208
231
  Note we used the SITE_ROOT constant we defined in the init.rb file, how you get the full path to your template may vary depending on your setup.
209
232
 
210
- See the Writing a Form Template Guide for more information. (Coming Soon)
233
+ See the Writing a Form Template Guide for more information. (Coming Soon)
234
+
235
+
236
+ ## Configuring Togo::Admin
237
+
238
+ There are currently only a couple configuration options for Togo Admin which affect runtime, but you can also pass in any
239
+ arbitrary configuration parameters and access them through custom templates in a global config hash. In our example, let's open
240
+ init.rb and configure Togo::Admin:
241
+
242
+ #init.rb:
243
+
244
+ SITE_ROOT = File.dirname(File.expand_path(__FILE__))
245
+ ... snipped ...
246
+
247
+ Togo::Admin.configure({:site_title => "My Admin Title", :my_custom_config => "Custom Config Value"})
248
+
249
+
250
+ If you had a custom template for your property and wanted to access :my_custom_config:
251
+
252
+ <%= config[:my_custom_config] %>
253
+
254
+
255
+ ### Authentication
256
+
257
+ By default Togo is not protected by an authentication method. If you're going to run Togo on a public web server, you'll
258
+ most likely want to protect it. Luckily you can tell Togo to use any object you desire to autenticate against.
259
+
260
+ First, define any object that responds to two methods:
261
+
262
+ #### self.authenticate(username, password)
263
+
264
+ A class method that Togo Admin will call, passing in a username and password. If successful, You must pass back an instance
265
+ of your object with a property called authenticated? set to true. If authentication fails, you can still pass back an
266
+ instance of your object, but with authenticated? returning false, or can return nil.
267
+
268
+ #### authenticated?
269
+
270
+ What Togo Admin will ask an instance of your object before every request. Must return true or false.
271
+
272
+ If the Togo Admin authenticates the user successfully, it will store the instance of that object in the session and check it
273
+ before each request to see if the user is authenticated or not.
274
+
275
+ ### Example User object for authentication
276
+
277
+ Here is an trivial example using a DataMapper object to authenticate a user:
278
+
279
+
280
+ class User
281
+
282
+ include DataMapper::Resource
283
+ property :id, Serial
284
+ property :username, String
285
+ property :password, String
286
+
287
+ attr_accessor :authenticated
288
+
289
+ def authenticated?
290
+ authenticated || false
291
+ end
292
+
293
+ def self.authenticate(u, p)
294
+ u = first(:username => u, :password => p)
295
+ u.authenticated = true if u
296
+ u
297
+ end
298
+
299
+ end
300
+
301
+
302
+ Note that the object does *not* have to have the Togo Model module included to work, unless you want it to.
303
+
304
+ ### Enabling Authentication
305
+
306
+ Finally tell Togo Admin to authenticate against your User object by configuring the Togo::Admin
307
+ application at runtime, either in your init.rb or togo-admin-config.rb file, like so:
308
+
309
+ Togo::Admin.configure({:auth_model => User})
310
+
311
+ It's up to you how to to authenticate the user: Local Database, LDAP, Kerberos, etc.
@@ -6,7 +6,7 @@ module Togo
6
6
  include Helpers
7
7
 
8
8
  before do
9
- if not logged_in? and request.path != '/login'
9
+ if not logged_in? and request.path != @path_prefix + '/login'
10
10
  redirect "/login"
11
11
  else
12
12
  @model = Togo.const_get(params[:model]) if params[:model]
@@ -42,4 +42,16 @@ module Helpers
42
42
  config[:auth_model] and session[:user] and session[:user].authenticated?
43
43
  end
44
44
 
45
+ def admin_url(url)
46
+ [@path_prefix, url].join
47
+ end
48
+
49
+ def return_url
50
+ if request.referer
51
+ request.referer.gsub(/^http:\/\/.*?\/(.*?)$/,"/\\1").gsub(Regexp.new("^#{@path_prefix}"), '')
52
+ else
53
+ admin_url('/' + @model.name)
54
+ end
55
+ end
56
+
45
57
  end
@@ -15,7 +15,7 @@ h3.error {
15
15
  font-size: 14px;
16
16
  }
17
17
  body {
18
- background: #efefef url(/img/login-bg.png) top left repeat-x;
18
+ background: #efefef url(../img/login-bg.png) top left repeat-x;
19
19
  font: 100% helvetica, arial, sans-serif;
20
20
  margin: 0;
21
21
  padding: 0;
@@ -29,7 +29,7 @@ body {
29
29
  -moz-border-radius: 4px;
30
30
  }
31
31
  #container h1 {
32
- background: #586069 url(/img/bg-header.png) repeat-x top left;
32
+ background: #586069 url(../img/bg-header.png) repeat-x top left;
33
33
  font-size: 24px;
34
34
  color: #FFF;
35
35
  text-shadow: 1px 1px 1px #5e6a76;
@@ -75,7 +75,7 @@ body {
75
75
  }
76
76
  #container form button {
77
77
  border: 1px solid #549300;
78
- background: #61a801 url(/img/button-bg-save.png) repeat-x top left;
78
+ background: #61a801 url(../img/button-bg-save.png) repeat-x top left;
79
79
  -webkit-border-radius: 4px;
80
80
  -moz-border-radius: 4px;
81
81
  padding: 5px 10px;
@@ -85,7 +85,7 @@ body {
85
85
  color: #FFF;
86
86
  }
87
87
  #container form button:hover {
88
- background: #69b802 url(/img/button-bg-save.png) repeat-x -30px left;
88
+ background: #69b802 url(../img/button-bg-save.png) repeat-x -30px left;
89
89
  text-shadow: 0 1px 1px #538A00;
90
90
  cursor: pointer;
91
91
  }
@@ -8,7 +8,7 @@ body {
8
8
  padding: 0;
9
9
  }
10
10
  .error {
11
- background: #800000 url(/img/button-bg-delete.png) repeat-x top left;
11
+ background: #800000 url(../img/button-bg-delete.png) repeat-x top left;
12
12
  text-shadow: 0 1px 1px #710000;
13
13
  border: 1px solid #a12f19;
14
14
  -webkit-border-radius: 2px;
@@ -23,7 +23,7 @@ h3.error {
23
23
  background: #f5f5f5;
24
24
  }
25
25
  #header {
26
- background: #586069 url(/img/bg-header.png) repeat-x top left;
26
+ background: #586069 url(../img/bg-header.png) repeat-x top left;
27
27
  }
28
28
  #header h1 {
29
29
  font-size: 24px;
@@ -110,7 +110,7 @@ h1:hover > #model-select {
110
110
  -webkit-box-shadow: -2px 0 20px rgba(51,51,51,0.3);
111
111
  }
112
112
  #content #subhead {
113
- background: #e0e0e0 url(/img/subhead-bg.png) repeat-x top left;
113
+ background: #e0e0e0 url(../img/subhead-bg.png) repeat-x top left;
114
114
  border-bottom: 1px solid #d4d2d2;
115
115
  padding-left: 20px;
116
116
  }
@@ -120,7 +120,7 @@ h1:hover > #model-select {
120
120
  line-height: 44px;
121
121
  margin: 0;
122
122
  color: #5e6a76;
123
- background-image: url(/img/arrow-down.png);
123
+ background-image: url(../img/arrow-down.png);
124
124
  background-position: 91% 50%;
125
125
  background-repeat: no-repeat;
126
126
  border-right: 1px solid #d4d2d2;
@@ -170,10 +170,10 @@ h1:hover > #model-select {
170
170
  padding-right: 15px;
171
171
  }
172
172
  #content table th a.desc {
173
- background: transparent url(/img/arrows.png) no-repeat 2px right;
173
+ background: transparent url(../img/arrows.png) no-repeat 2px right;
174
174
  }
175
175
  #content table th a.asc {
176
- background: transparent url(/img/arrows.png) no-repeat right -16px;
176
+ background: transparent url(../img/arrows.png) no-repeat right -16px;
177
177
  }
178
178
  #content table td {
179
179
  padding: 10px;
@@ -196,7 +196,7 @@ h1:hover > #model-select {
196
196
  }
197
197
  #content table tr.disabled td {
198
198
  color: #888;
199
- background: transparent url(/img/disabled.png);
199
+ background: transparent url(../img/disabled.png);
200
200
  }
201
201
  #content table tr.disabled td span.address,
202
202
  #content table tr.disabled td a {
@@ -208,7 +208,7 @@ h1:hover > #model-select {
208
208
  width: 20px;
209
209
  }
210
210
  #content div.actions {
211
- background: #ccc url(/img/footer-bg.png) repeat-x top left;
211
+ background: #ccc url(../img/footer-bg.png) repeat-x top left;
212
212
  position: absolute;
213
213
  left: 0;
214
214
  right: 0;
@@ -221,7 +221,7 @@ h1:hover > #model-select {
221
221
  }
222
222
  #content div.actions button {
223
223
  border: 1px solid #549300;
224
- background: #61a801 url(/img/button-bg-save.png) repeat-x top left;
224
+ background: #61a801 url(../img/button-bg-save.png) repeat-x top left;
225
225
  -webkit-border-radius: 4px;
226
226
  -moz-border-radius: 4px;
227
227
  padding: 5px 10px;
@@ -231,12 +231,12 @@ h1:hover > #model-select {
231
231
  color: #FFF;
232
232
  }
233
233
  #content div.actions button:hover {
234
- background: #69b802 url(/img/button-bg-save.png) repeat-x -30px left;
234
+ background: #69b802 url(../img/button-bg-save.png) repeat-x -30px left;
235
235
  text-shadow: 0 1px 1px #538A00;
236
236
  cursor: pointer;
237
237
  }
238
238
  #content div.actions button#delete-button {
239
- background: #800000 url(/img/button-bg-delete.png) repeat-x top left;
239
+ background: #800000 url(../img/button-bg-delete.png) repeat-x top left;
240
240
  text-shadow: 0 1px 1px #710000;
241
241
  border: 1px solid #a12f19;
242
242
  }
@@ -247,7 +247,7 @@ h1:hover > #model-select {
247
247
  text-shadow: none;
248
248
  }
249
249
  #content div.actions button#delete-button:hover {
250
- background: #910000 url(/img/button-bg-delete.png) repeat-x -30px left;
250
+ background: #910000 url(../img/button-bg-delete.png) repeat-x -30px left;
251
251
  text-shadow: 0 1px 1px #A12F19;
252
252
  }
253
253
  #content div.actions button#delete-button:disabled:hover {
@@ -179,6 +179,7 @@ var AssociationManager = function(opts) {
179
179
  this.pageCount = Math.ceil(this.count/10);
180
180
  this.pageNumber = 1;
181
181
  this.relatedIds = {};
182
+ this.pathPrefix = opts.pathPrefix;
182
183
 
183
184
  for (var i = 0, len = opts.related_ids.length; i < len; i++) {
184
185
  this.relatedIds[opts.related_ids[i]] = true;
@@ -238,7 +239,7 @@ AssociationManager.prototype.adjustRelatedList = function() {
238
239
  getElementsByClassName('related_ids', this.fieldset)[0].value = selected.join(',');
239
240
  var handler = this;
240
241
  ajax({
241
- url: "/search/" + this.model + "?ids=" + selected.slice(0,10).join(','),
242
+ url: (this.pathPrefix + "/search/" + this.model + "?ids=" + selected.slice(0,10).join(',')),
242
243
  success: function(data) {
243
244
  handler.searchResultsHandler(handler)(data);
244
245
  handler.count = selected.length;
@@ -255,7 +256,7 @@ AssociationManager.prototype.adjustRelatedList = function() {
255
256
 
256
257
  AssociationManager.prototype.setToModifyState = function() {
257
258
  ajax({
258
- url: "/search/" + this.model,
259
+ url: (this.pathPrefix + "/search/" + this.model),
259
260
  success: this.searchResultsHandler(this)
260
261
  });
261
262
  };
@@ -310,7 +311,7 @@ AssociationManager.prototype.pagingHandler = function(handler) {
310
311
  handler.pageNumberField.innerHTML = handler.pageNumber;
311
312
  var offset = (handler.pageNumber-1)*10;
312
313
  ajax({
313
- url: "/search/" + handler.model + "?q=" + q + "&limit=10&offset=" + offset,
314
+ url: (handler.pathPrefix + "/search/" + handler.model + "?q=" + q + "&limit=10&offset=" + offset),
314
315
  success: handler.searchResultsHandler(handler)
315
316
  });
316
317
  }
@@ -382,7 +383,7 @@ AssociationManager.prototype.searchHandler = function(handler) {
382
383
  handler.pageNumber = 1;
383
384
  var offset = (handler.pageNumber-1)*10;
384
385
  ajax({
385
- url: "/search/" + handler.model + "?q=" + q + "&limit=10&offset=" + offset,
386
+ url: (handler.pathPrefix + "/search/" + handler.model + "?q=" + q + "&limit=10&offset=" + offset),
386
387
  success: handler.searchResultsHandler(handler)
387
388
  });
388
389
  return false;
@@ -400,4 +401,22 @@ AssociationManager.prototype.searchInputHandler = function(handler, action) {
400
401
  this.value = '';
401
402
  }
402
403
  };
403
- };
404
+ };
405
+
406
+ var Togo = (function() {
407
+
408
+ var config = {};
409
+
410
+ var configure = function(opts) {
411
+ for (k in opts) {
412
+ config[k] = opts[k];
413
+ }
414
+ };
415
+
416
+ return {
417
+ config: config,
418
+ configure: configure
419
+ };
420
+
421
+ }());
422
+
@@ -1,7 +1,7 @@
1
- <form action="/update/<%= @model.name %>/<%= @content.id %>" method="post" id="edit-form">
2
- <input type="hidden" name="return_url" value="<%= request.referer || '/locations' %>" />
1
+ <form action="<%= admin_url('/update/' + @model.name + '/' + @content.id.to_s) %>" method="post" id="edit-form">
2
+ <input type="hidden" name="return_url" value="<%= return_url %>" />
3
3
  <div id="subhead">
4
- <h1><a href="/<%= @model.name %>"><%= @model.display_name %></a> / Edit</h1>
4
+ <h1><a href="<%= admin_url('/' + @model.name) %>"><%= @model.display_name %></a> / Edit</h1>
5
5
  </div>
6
6
  <div id="main">
7
7
  <% if @errors %><div id="save_errors" class="errors"><%= @errors %></div><% end %>
@@ -15,4 +15,4 @@
15
15
  <button type="submit" class="save">Save <%= @model.display_name.singularize %></button>
16
16
  </div>
17
17
  </form>
18
- <script type="text/javascript" src="/js/edit.js"></script>
18
+ <script type="text/javascript" src="<%= admin_url('/js/edit.js') %>"></script>
@@ -4,7 +4,7 @@
4
4
  <ul>
5
5
  <% Togo.models.each do |m| %>
6
6
  <% next if m == @model %>
7
- <li><a href="/<%= m.name %>"><%= m.display_name %></a></li>
7
+ <li><a href="<%= admin_url('/' + m.name) %>"><%= m.display_name %></a></li>
8
8
  <% end %>
9
9
  </ul>
10
10
  </div>
@@ -35,7 +35,7 @@
35
35
  <td class="checkbox"><input type="checkbox" name="selection[<%= c.id %>]" value="1" id="selection_<%= c.id %>" /></td>
36
36
  <% @model.get_list_properties.each_with_index do |p,i| %>
37
37
  <% v = c.send(p.name.to_sym) || '-' %>
38
- <td><% if i == 0 %><a href="/edit/<%= @model.name %>/<%= c.id %>"><%= v %></a><% else %><%= v %><% end %></td>
38
+ <td><% if i == 0 %><a href="<%= admin_url('/edit/' + @model.name + '/' + c.id.to_s) %>"><%= v %></a><% else %><%= v %><% end %></td>
39
39
  <% end %>
40
40
  </tr>
41
41
  <% end %>
@@ -45,10 +45,10 @@
45
45
  </div>
46
46
  </div>
47
47
  <div class="actions">
48
- <form action="/delete/<%= @model.name %>" method="post" id="delete-form">
48
+ <form action="<%= admin_url('/delete/' + @model.name) %>" method="post" id="delete-form">
49
49
  <input type="hidden" id="delete-list" name="id" value="" />
50
50
  <button type="submit" id="delete-button" disabled="disabled" onclick="return confirm('Are you sure you want to delete these items?');">Delete</button>
51
51
  </form>
52
- <button onclick="window.location='/new/<%= @model.name %>'; return false;">New...</button>
52
+ <button onclick="window.location='<%= admin_url('/new/' + @model.name) %>'; return false;">New...</button>
53
53
  </div>
54
- <script type="text/javascript" src="/js/index.js"></script>
54
+ <script type="text/javascript" src="<%= admin_url('/js/index.js') %>"></script>
@@ -3,8 +3,13 @@
3
3
  <head>
4
4
  <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
5
5
  <title><%= config[:site_title] %></title>
6
- <link rel="stylesheet" href="/css/screen.css" type="text/css" media="screen" />
7
- <script type="text/javascript" src="/js/togo.js"></script>
6
+ <link rel="stylesheet" href="<%= admin_url('/css/screen.css') %>" type="text/css" media="screen" />
7
+ <script type="text/javascript" src="<%= admin_url('/js/togo.js') %>"></script>
8
+ <script type="text/javascript">
9
+ Togo.configure({
10
+ pathPrefix: '<%= @path_prefix %>'
11
+ });
12
+ </script>
8
13
  </head>
9
14
  <body id="<%= @body_id %>">
10
15
  <div id="container">
@@ -14,7 +19,7 @@
14
19
  <div id="system-menu">
15
20
  <ul>
16
21
  <% config[:system_menu].each do |s| %>
17
- <li><a href="<%= s[:url] %>"><%= s[:title] %></a></li>
22
+ <li><a href="<%= admin_url(s[:url]) %>"><%= s[:title] %></a></li>
18
23
  <% end %>
19
24
  </ul>
20
25
  </div>
@@ -3,9 +3,9 @@
3
3
  <head>
4
4
  <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
5
5
  <title><%= config[:site_title] %> - Login</title>
6
- <link rel="stylesheet" href="/css/login.css" type="text/css" media="screen" />
7
- <script type="text/javascript" src="/js/togo.js"></script>
8
- <script type="text/javascript" src="/js/login.js"></script>
6
+ <link rel="stylesheet" href="<%= admin_url('/css/login.css') %>" type="text/css" media="screen" />
7
+ <script type="text/javascript" src="<%= admin_url('/js/togo.js') %>"></script>
8
+ <script type="text/javascript" src="<%= admin_url('/js/login.js') %>"></script>
9
9
  </head>
10
10
  <body>
11
11
  <div id="container">
@@ -13,7 +13,7 @@
13
13
  <% if flash[:error] %>
14
14
  <h3 class="error"><%= flash[:error] %></h3>
15
15
  <% end %>
16
- <form action="/login" method="post">
16
+ <form action="<%= admin_url('/login') %>" method="post">
17
17
  <fieldset>
18
18
  <input type="text" name="username" value="Username" id="username" class="placeholder" />
19
19
  </fieldset>
@@ -28,6 +28,6 @@
28
28
  login.init();
29
29
  </script>
30
30
  </div>
31
- <img src="/img/shadow.png" alt="" style="display: block; margin: 0 auto;" />
31
+ <img src="<%= admin_url('/img/shadow.png') %>" alt="" style="display: block; margin: 0 auto;" />
32
32
  </body>
33
33
  </html>
@@ -1,7 +1,7 @@
1
- <form action="/create/<%= @model.name %>" method="post" id="edit-form">
2
- <input type="hidden" name="return_url" value="<%= request.referer || '/locations' %>" />
1
+ <form action="<%= admin_url('/create/' + @model.name) %>" method="post" id="edit-form">
2
+ <input type="hidden" name="return_url" value="<%= return_url %>" />
3
3
  <div id="subhead">
4
- <h1><a href="/<%= @model.name %>"><%= @model.display_name %></a> / New</h1>
4
+ <h1><a href="<%= admin_url('/' + @model.name) %>"><%= @model.display_name %></a> / New</h1>
5
5
  </div>
6
6
  <div id="main">
7
7
  <% if @errors %><div id="save_errors" class="errors"><%= @errors %></div><% end %>
@@ -15,5 +15,5 @@
15
15
  <button type="submit" class="create">Create</button>
16
16
  </div>
17
17
  </form>
18
- <script type="text/javascript" src="/js/edit.js"></script>
18
+ <script type="text/javascript" src="<%= admin_url('/js/edit.js') %>"></script>
19
19
 
@@ -28,6 +28,8 @@ module Togo
28
28
 
29
29
  def initialize(opts = {})
30
30
  @view_path = opts[:view_path] || 'views'
31
+ @path_prefix = opts[:path_prefix]
32
+ @path_prefix_regexp = Regexp.new("^#{@path_prefix}")
31
33
  ENV['RACK_ENV'] = (opts[:environment] || :development) if not ENV['RACK_ENV']
32
34
  end
33
35
 
@@ -50,6 +52,8 @@ module Togo
50
52
  def answer(type,path)
51
53
  method = nil
52
54
  @params = symbolize_keys(@request.GET.dup.merge!(@request.POST.dup))
55
+ path = path.gsub(@path_prefix_regexp,'')
56
+ path = '/' if path == ''
53
57
  self.class.routes[type].each do |p,k,m|
54
58
  if match = p.match(path)
55
59
  method = m
@@ -85,7 +89,7 @@ module Togo
85
89
 
86
90
  def redirect(location, opts = {})
87
91
  @response.status = (opts[:status] || 301)
88
- @response.headers['Location'] = location
92
+ @response.headers['Location'] = [@path_prefix, location].join
89
93
  @response.finish
90
94
  end
91
95
 
@@ -123,6 +127,7 @@ module Togo
123
127
  subclass.send(:include, Rack::Utils)
124
128
  %w{GET POST}.each{|v| subclass.routes[v] = []}
125
129
  subclass.config = {
130
+ :path_prefix => '',
126
131
  :public_path => 'public',
127
132
  :static_urls => ['/css','/js','/img'],
128
133
  :port => 8080,
@@ -177,7 +182,7 @@ module Togo
177
182
  end
178
183
  end
179
184
  end
180
- builder.use Rack::Static, :urls => opts[:static_urls], :root => opts[:public_path]
185
+ builder.use Togo::Static, :urls => opts[:static_urls], :root => opts[:public_path], :path_prefix => opts[:path_prefix]
181
186
  if opts[:sessions] or opts[:auth_model]
182
187
  builder.use Rack::Session::Cookie
183
188
  opts[:sessions] = true
@@ -194,4 +199,34 @@ module Togo
194
199
 
195
200
  end # Dispatch
196
201
 
202
+ class Static
203
+
204
+ def initialize(app, options={})
205
+ @app = app
206
+ @path_prefix = options[:path_prefix] || ''
207
+ @urls = options[:urls] || ["/favicon.ico"]
208
+ root = options[:root] || Dir.pwd
209
+ @file_server = Rack::File.new(root)
210
+ end
211
+
212
+ def call(env)
213
+ path = env["PATH_INFO"]
214
+ path = path.gsub(Regexp.new("^#{@path_prefix}"), '') if @path_prefix > ''
215
+
216
+ unless @urls.kind_of? Hash
217
+ can_serve = @urls.any? { |url| path.index(url) == 0 }
218
+ else
219
+ can_serve = @urls.key? path
220
+ end
221
+
222
+ if can_serve
223
+ env["PATH_INFO"] = (@urls.kind_of?(Hash) ? @urls[path] : path)
224
+ @file_server.call(env)
225
+ else
226
+ @app.call(env)
227
+ end
228
+ end
229
+
230
+ end
231
+
197
232
  end # Togo
@@ -14,7 +14,6 @@ class PropertyWrapper
14
14
  end
15
15
 
16
16
  def label
17
- puts @model.property_options.inspect
18
17
  @model.property_options[@name][:label] rescue humanized_name
19
18
  end
20
19
 
@@ -58,6 +58,7 @@
58
58
  model: '<%= related_model.name %>',
59
59
  fields: <%= related_model.get_list_properties.map(&:name).to_json %>,
60
60
  count: <%= content_count %>,
61
- related_ids: <%= related_ids.to_json %>
61
+ related_ids: <%= related_ids.to_json %>,
62
+ pathPrefix: Togo.config.pathPrefix
62
63
  });
63
64
  </script>
@@ -60,6 +60,7 @@
60
60
  model: '<%= related_model.name %>',
61
61
  fields: <%= related_model.get_list_properties.map(&:name).to_json %>,
62
62
  count: <%= content_count %>,
63
- related_ids: <%= related_ids.to_json %>
63
+ related_ids: <%= related_ids.to_json %>,
64
+ pathPrefix: Togo.config.pathPrefix
64
65
  });
65
66
  </script>
@@ -60,7 +60,8 @@
60
60
  model: '<%= related_model.name %>',
61
61
  fields: <%= related_model.get_list_properties.map(&:name).to_json %>,
62
62
  count: <%= content_count %>,
63
- related_ids: <%= related_ids.to_json %>
63
+ related_ids: <%= related_ids.to_json %>,
64
+ pathPrefix: Togo.config.pathPrefix
64
65
  });
65
66
  </script>
66
67
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: togo
3
3
  version: !ruby/object:Gem::Version
4
- hash: 3
4
+ hash: 1
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 6
9
- - 2
10
- version: 0.6.2
9
+ - 3
10
+ version: 0.6.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Matt King
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-11-18 00:00:00 -08:00
18
+ date: 2010-11-21 00:00:00 -08:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency