togo 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +210 -0
- data/lib/togo/admin/admin.rb +6 -25
- data/lib/togo/admin/helpers.rb +57 -0
- data/lib/togo/admin/public/css/screen.css +329 -190
- data/lib/togo/admin/public/img/arrow-down.png +0 -0
- data/lib/togo/admin/public/img/arrows.png +0 -0
- data/lib/togo/admin/public/img/bg-header.png +0 -0
- data/lib/togo/admin/public/img/button-bg-delete.png +0 -0
- data/lib/togo/admin/public/img/button-bg-save.png +0 -0
- data/lib/togo/admin/public/img/footer-bg.png +0 -0
- data/lib/togo/admin/public/img/subhead-bg.png +0 -0
- data/lib/togo/admin/public/js/edit.js +23 -24
- data/lib/togo/admin/public/js/index.js +3 -2
- data/lib/togo/admin/public/js/togo.js +46 -14
- data/lib/togo/admin/views/_paging.erb +14 -0
- data/lib/togo/admin/views/edit.erb +6 -9
- data/lib/togo/admin/views/index.erb +44 -44
- data/lib/togo/admin/views/layout.erb +14 -16
- data/lib/togo/admin/views/new.erb +5 -2
- data/lib/togo/model/model.rb +20 -38
- data/lib/togo/model/relationship_manager/many_to_one.rb +19 -0
- data/lib/togo/model/relationship_manager/one_to_many.rb +19 -0
- data/lib/togo/model/relationship_manager/relationship_manager.rb +40 -0
- data/lib/togo/model/types/belongs_to.erb +42 -40
- data/lib/togo/model/types/has_n.erb +44 -42
- data/lib/togo/model/types/many_to_many.erb +44 -42
- metadata +17 -7
- data/README +0 -29
data/README.md
ADDED
@@ -0,0 +1,210 @@
|
|
1
|
+
# Introducing Togo
|
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.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
gem install togo
|
9
|
+
|
10
|
+
## Tutorial
|
11
|
+
|
12
|
+
Togo works by a simply including a line in your model definition (currently, Togo only works for DataMapper). By including Togo in your model,
|
13
|
+
it will be automatically integrated into the Togo admin application, giving you all the CRUD actions as well as listing and search. It even
|
14
|
+
works with associations.
|
15
|
+
|
16
|
+
### Setting up a simple application
|
17
|
+
|
18
|
+
We'll use a combination of Sinatra, DataMapper and Sqlite for our tutorial. You actually don't need Sinatra to run the Togo admin application, however this
|
19
|
+
gives a good idea of how Togo can be used in conjuction with an existing project.
|
20
|
+
|
21
|
+
You'll need the following gems installed:
|
22
|
+
|
23
|
+
pre. dm-core dm-serializer sinatra dm-sqlite3-adapter
|
24
|
+
|
25
|
+
Next, make a directory for your project like so:
|
26
|
+
|
27
|
+
|
28
|
+
togo-example/
|
29
|
+
|
|
30
|
+
-- models/
|
31
|
+
|
|
32
|
+
-- views/
|
33
|
+
|
|
34
|
+
-- init.rb
|
35
|
+
|
|
36
|
+
-- app.rb
|
37
|
+
|
|
38
|
+
-- example.db
|
39
|
+
|
40
|
+
|
41
|
+
### Set up your models
|
42
|
+
|
43
|
+
Inside the models directory, create a blog_entry.rb and comment.rb file, and drop in the following:
|
44
|
+
|
45
|
+
|
46
|
+
#blog_entry.rb:
|
47
|
+
|
48
|
+
class BlogEntry
|
49
|
+
|
50
|
+
include DataMapper::Resource
|
51
|
+
include Togo::DataMapper::Model
|
52
|
+
|
53
|
+
property :id, Serial
|
54
|
+
property :title, String
|
55
|
+
property :body, Text
|
56
|
+
property :date, DateTime
|
57
|
+
|
58
|
+
has n, :comments
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
#comment.rb:
|
63
|
+
|
64
|
+
class Comment
|
65
|
+
include DataMapper::Resource
|
66
|
+
include Togo::DataMapper::Model
|
67
|
+
|
68
|
+
property :id, Serial
|
69
|
+
property :name, String
|
70
|
+
property :email, String
|
71
|
+
property :body, Text
|
72
|
+
property :date, DateTime
|
73
|
+
property :blog_entry_id, Integer
|
74
|
+
|
75
|
+
belongs_to :blog_entry
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
### Create an init file
|
80
|
+
|
81
|
+
Togo doesn't set up your database connection or initialize anything needed by your models for you, so you'll have to tell
|
82
|
+
it what to do. There are a couple ways of doing it: put the code in a file of your choice and tell Togo to include
|
83
|
+
it at runtime, or place the code in a file called togo-admin-config.rb in the same directory that you will run Togo from.
|
84
|
+
|
85
|
+
In this example, we'll put our initialization in the init.rb file and tell Togo to include it at boot time.
|
86
|
+
|
87
|
+
|
88
|
+
#init.rb:
|
89
|
+
|
90
|
+
SITE_ROOT = File.dirname(File.expand_path(__FILE__))
|
91
|
+
%w(dm-core dm-migrations togo).each{|l| require l}
|
92
|
+
Dir.glob(File.join(SITE_ROOT,'models','*.rb')).each{|f| require f}
|
93
|
+
DataMapper::Logger.new(STDOUT, :debug)
|
94
|
+
DataMapper.setup(:default, "sqlite3://#{Dir.pwd}/example.db")
|
95
|
+
DataMapper.auto_upgrade!
|
96
|
+
|
97
|
+
|
98
|
+
### You're ready to start using Togo
|
99
|
+
|
100
|
+
At this point you can fire up the Togo admin application. Remember we have to include our init.rb file:
|
101
|
+
|
102
|
+
togo-admin -r init.rb
|
103
|
+
|
104
|
+
Open up http://0.0.0.0:8080 in your browser and have a blast! Have a look at our CMS User's Guide for a walkthrough of base CMS functionality (Coming Soon).
|
105
|
+
|
106
|
+
## Customizing Togo
|
107
|
+
|
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.
|
110
|
+
|
111
|
+
### Telling Togo which fields to use
|
112
|
+
|
113
|
+
By default Togo will look at your model and include all available fields in the admin, but you can also tell it specifically which fields
|
114
|
+
to use for both the list view and form view.
|
115
|
+
|
116
|
+
#### Changing the list view
|
117
|
+
|
118
|
+
If you only want the title and date field to appear in the list view of blog entries, just add in to your model definition:
|
119
|
+
|
120
|
+
|
121
|
+
class BlogEntry
|
122
|
+
|
123
|
+
include DataMapper::Resource
|
124
|
+
include Togo::DataMapper::Model
|
125
|
+
... snipped ...
|
126
|
+
|
127
|
+
list_properties :title, :date
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
Togo will display the fields in the order given. So if you wanted date first some reason, you could just do
|
133
|
+
|
134
|
+
pre. list_properties :date, :title
|
135
|
+
|
136
|
+
And date will be listed in the first column.
|
137
|
+
|
138
|
+
#### Changing the form view (New and Edit views)
|
139
|
+
|
140
|
+
Just as you can change the list view fields, you can also tell Togo which fields to show in form view and in which order.
|
141
|
+
|
142
|
+
|
143
|
+
class BlogEntry
|
144
|
+
|
145
|
+
include DataMapper::Resource
|
146
|
+
include Togo::DataMapper::Model
|
147
|
+
... snipped ...
|
148
|
+
|
149
|
+
list_properties :title, :date
|
150
|
+
form_properties :title, :date, :body
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
Associations also can be used in list and form property declarations:
|
156
|
+
|
157
|
+
|
158
|
+
class BlogEntry
|
159
|
+
|
160
|
+
include DataMapper::Resource
|
161
|
+
include Togo::DataMapper::Model
|
162
|
+
... snipped ...
|
163
|
+
|
164
|
+
list_properties :title, :date
|
165
|
+
form_properties :title, :date, :body, :comments
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
#### Configuring properties
|
171
|
+
|
172
|
+
There are some options you can change on each field individually by using the configure_property declaration.
|
173
|
+
|
174
|
+
Here you can customize the label:
|
175
|
+
|
176
|
+
class BlogEntry
|
177
|
+
|
178
|
+
include DataMapper::Resource
|
179
|
+
include Togo::DataMapper::Model
|
180
|
+
... snipped ...
|
181
|
+
|
182
|
+
list_properties :title, :date
|
183
|
+
form_properties :title, :date, :body
|
184
|
+
|
185
|
+
configure_property :title, :label => "Title (keep it short and sweet)"
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
Each property type has it's own default form template that can be overridden simply by specifying a full path to the template (only ERB is currently supported):
|
191
|
+
|
192
|
+
|
193
|
+
class BlogEntry
|
194
|
+
|
195
|
+
include DataMapper::Resource
|
196
|
+
include Togo::DataMapper::Model
|
197
|
+
... snipped ...
|
198
|
+
|
199
|
+
list_properties :title, :date
|
200
|
+
form_properties :title, :date, :body
|
201
|
+
|
202
|
+
configure_property :title, :label => "Title (keep it short and sweet)"
|
203
|
+
configure_property :body, :template => File.join(SITE_ROOT, 'views', 'custom_body.erb')
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
|
208
|
+
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
|
+
|
210
|
+
See the Writing a Form Template Guide for more information. (Coming Soon)
|
data/lib/togo/admin/admin.rb
CHANGED
@@ -1,24 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
def paging_links(page, count, qs = {})
|
4
|
-
prev_link, next_link = 'Previous', 'Next'
|
5
|
-
if not qs.blank?
|
6
|
-
qs = qs.keys.collect{|k|
|
7
|
-
[k,escape(qs[k])].join('=') if not qs[k].blank?
|
8
|
-
}.compact.join('&')
|
9
|
-
qs = nil if qs.blank?
|
10
|
-
end
|
11
|
-
|
12
|
-
if not page == 1
|
13
|
-
prev_link = "<a href=\"?p=#{[page-1, qs].compact.join('&')}\" rel=\"previous\">#{prev_link}</a>"
|
14
|
-
end
|
15
|
-
if not page == count and count > 1
|
16
|
-
next_link = "<a href=\"?p=#{[page+1, qs].compact.join('&')}\" rel=\"next\">#{next_link}</a>"
|
17
|
-
end
|
18
|
-
[prev_link, next_link]
|
19
|
-
end
|
20
|
-
|
21
|
-
end
|
1
|
+
require 'helpers'
|
22
2
|
|
23
3
|
module Togo
|
24
4
|
class Admin < Dispatch
|
@@ -38,9 +18,10 @@ module Togo
|
|
38
18
|
@p = (params[:p] || 1).to_i
|
39
19
|
@limit = 50
|
40
20
|
@offset = @limit*(@p-1)
|
21
|
+
@order = (params[:o] || "id.desc").split('.').map(&:to_sym)
|
41
22
|
@count = (@q.blank? ? @model.all : @model.search(:q => @q)).size
|
42
23
|
@page_count = @count == 0 ? 1 : (@count.to_f/@limit.to_f).ceil
|
43
|
-
@criteria = {:limit => @limit, :offset => @offset}
|
24
|
+
@criteria = {:limit => @limit, :offset => @offset, :order => @order[0].send(@order[1])}
|
44
25
|
@content = @q.blank? ? @model.all(@criteria) : @model.search(@criteria.merge(:q => @q))
|
45
26
|
erb :index
|
46
27
|
end
|
@@ -54,7 +35,7 @@ module Togo
|
|
54
35
|
@content = @model.stage_content(@model.new,params)
|
55
36
|
begin
|
56
37
|
raise "Could not save content" if not @content.save
|
57
|
-
redirect "/#{@model.name}"
|
38
|
+
redirect params[:return_url] || "/#{@model.name}"
|
58
39
|
rescue => detail
|
59
40
|
@errors = detail.to_s
|
60
41
|
erb :new
|
@@ -70,7 +51,7 @@ module Togo
|
|
70
51
|
@content = @model.stage_content(@model.get(params[:id]),params)
|
71
52
|
begin
|
72
53
|
raise "Could not save content" if not @content.save
|
73
|
-
redirect "/#{@model.name}"
|
54
|
+
redirect params[:return_url] || "/#{@model.name}"
|
74
55
|
rescue => detail
|
75
56
|
@errors = detail.to_s
|
76
57
|
erb :edit
|
@@ -83,7 +64,7 @@ module Togo
|
|
83
64
|
@items.each do |i|
|
84
65
|
@model.delete_content(i)
|
85
66
|
end
|
86
|
-
redirect "/#{@model.name}"
|
67
|
+
redirect params[:return_url] || "/#{@model.name}"
|
87
68
|
rescue => detail
|
88
69
|
@errors = detail.to_s
|
89
70
|
@content = params[:q] ? @model.search(:q => params[:q]) : @model.all
|
data/lib/togo/admin/helpers.rb
CHANGED
@@ -0,0 +1,57 @@
|
|
1
|
+
module Helpers
|
2
|
+
|
3
|
+
class FlashHash
|
4
|
+
def initialize
|
5
|
+
@h = {}
|
6
|
+
@c = {}
|
7
|
+
end
|
8
|
+
def [](key)
|
9
|
+
return @c[key] if @c.keys.include?(key)
|
10
|
+
@c[key] = @h.delete(key) if @h.keys.include?(key)
|
11
|
+
end
|
12
|
+
def []=(key,val)
|
13
|
+
@h[key] = val
|
14
|
+
end
|
15
|
+
def sweep!
|
16
|
+
@c = {}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def hash_to_qs(h)
|
21
|
+
return nil if h.blank?
|
22
|
+
qs = h.keys.collect{|k|
|
23
|
+
[k,escape(h[k])].join('=') if not h[k].blank?
|
24
|
+
}.compact.join('&')
|
25
|
+
qs = nil if qs.blank?
|
26
|
+
qs
|
27
|
+
end
|
28
|
+
|
29
|
+
def paging_links(page, count, qs = {})
|
30
|
+
prev_link, next_link = 'Previous', 'Next'
|
31
|
+
qs = hash_to_qs(qs)
|
32
|
+
|
33
|
+
if not page == 1
|
34
|
+
prev_link = "<a href=\"?p=#{[page-1, qs].compact.join('&')}\" rel=\"previous\">#{prev_link}</a>"
|
35
|
+
end
|
36
|
+
if not page == count and count > 1
|
37
|
+
next_link = "<a href=\"?p=#{[page+1, qs].compact.join('&')}\" rel=\"next\">#{next_link}</a>"
|
38
|
+
end
|
39
|
+
[prev_link, next_link]
|
40
|
+
end
|
41
|
+
|
42
|
+
def column_head_link(property, current_order, qs = {})
|
43
|
+
qs = hash_to_qs(qs)
|
44
|
+
new_order = (current_order[0] == property.name.to_sym ? (current_order[1] == :asc ? "desc" : "asc") : "asc")
|
45
|
+
link_class = current_order[0] == property.name.to_sym ? new_order : ''
|
46
|
+
"<a href=\"?o=#{[(property.name.to_s+'.'+new_order.to_s),qs].compact.join('&')}\" class=\"#{link_class}\">#{property.name.to_s.humanize.titleize}</a>"
|
47
|
+
end
|
48
|
+
|
49
|
+
def partial(template, options={})
|
50
|
+
erb template, options.merge(:layout => false)
|
51
|
+
end
|
52
|
+
|
53
|
+
def active_menu(name)
|
54
|
+
request.path =~ /#{name}/ ? 'active' : ''
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|