sethyates-content_manager 0.4.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/README.rdoc +253 -1
  2. data/VERSION +1 -1
  3. data/content_manager.gemspec +38 -2
  4. data/doc/created.rid +1 -0
  5. data/doc/files/README_rdoc.html +487 -0
  6. data/doc/fr_class_index.html +26 -0
  7. data/doc/fr_file_index.html +27 -0
  8. data/doc/fr_method_index.html +26 -0
  9. data/doc/index.html +24 -0
  10. data/doc/rdoc-style.css +208 -0
  11. data/generators/component_scaffold/USAGE +28 -0
  12. data/generators/component_scaffold/component_scaffold_generator.rb +84 -0
  13. data/generators/component_scaffold/templates/controller.rb +85 -0
  14. data/generators/component_scaffold/templates/model.rb +5 -0
  15. data/generators/component_scaffold/templates/style.css +1 -0
  16. data/generators/component_scaffold/templates/view_edit.html.erb +13 -0
  17. data/generators/component_scaffold/templates/view_index.html.erb +22 -0
  18. data/generators/component_scaffold/templates/view_new.html.erb +12 -0
  19. data/generators/component_scaffold/templates/view_show.html.erb +3 -0
  20. data/generators/content_scaffold/USAGE +28 -0
  21. data/generators/content_scaffold/content_scaffold_generator.rb +83 -0
  22. data/generators/content_scaffold/templates/controller.rb +85 -0
  23. data/generators/content_scaffold/templates/model.rb +8 -0
  24. data/generators/content_scaffold/templates/view_edit.html.erb +13 -0
  25. data/generators/content_scaffold/templates/view_index.html.erb +22 -0
  26. data/generators/content_scaffold/templates/view_new.html.erb +12 -0
  27. data/generators/content_scaffold/templates/view_show.html.erb +8 -0
  28. data/lib/component.rb +28 -0
  29. data/lib/content/adapters/base.rb +79 -0
  30. data/lib/content/adapters/cabinet_adapter.rb +62 -0
  31. data/lib/content/adapters/tyrant_adapter.rb +73 -0
  32. data/lib/content/item.rb +178 -0
  33. data/lib/content/item_association_class_methods.rb +148 -0
  34. data/lib/content/item_class_methods.rb +71 -0
  35. data/lib/content/item_dirty_methods.rb +171 -0
  36. data/lib/content/item_finder_class_methods.rb +203 -0
  37. data/lib/content/manager.rb +105 -0
  38. data/lib/content/sublayout.rb +44 -0
  39. data/lib/content/template.rb +24 -0
  40. metadata +38 -2
@@ -1,9 +1,261 @@
1
1
  = Content::Manager
2
2
 
3
- A content manager. Currently prototype code. Use at your own risk.
3
+ == Overview
4
+
5
+ The Content::Manager plugin provides Content Management capability for Rails applications. Content::Manager is built on top of a Tokyo Tyrant (or Tokyo Cabinet) schema-less table database.
6
+ Using standard Rails functionality of models, views and controllers with some additional "glue", Content::Manager makes it straight-forward to build component-based, content-managed Rails applications.
7
+
8
+ == How it works
9
+
10
+ The Content Manager is invoked by the final route in the routes file:
11
+
12
+ map.content_item '*content_item_url', :controller => 'content', :action => 'show'
13
+
14
+ This route tells Rails to pass any URL (in the content_item_url parameter) to the show action of the ContentController.
15
+
16
+ When the show action in the ContentController is invoked (inherited from Content::Manager), the before_filter :current_content_item in the ApplicationController is invoked first. This causes the Content::Item specified by the URL in content_item_url to be loaded using Content::Item.find_by_url params[:content_item_url].
17
+
18
+ Back in the ContentController#show action, the current_content_item is checked if it is nil, if it is missing a template or if its template is missing a sublayout. In any of these cases, a 404 Not Found error is rendered (specified in app/views/errors/error404.html.erb).
19
+
20
+ Assuming we have a valid Content::Item with a template and a sublayout, then the containers are rendered (using prerender_containers, content_for, render_container and render_component in Content::Manager). The contents of each container are stored for later use by yield within the Sublayout. Finally, the Sublayout is rendered as a template using render :template. At this point, no layout is specified, so Sublayouts have to provide the full HTML. This may change in the future though.
21
+
22
+ The Content::Manager#render_component method requires special mention. This method operates by creating a new Rack request and handing the request off to the ActionController::Routing::Routes table. Any error from here is raised as a RuntimeError with the message being the full HTML returned from the error. In the case of Content::Manager, any errors from the components is returned as the page error.
23
+
24
+ == Creating a Content Item
25
+ 1. Create the content_scaffold.
26
+
27
+ $ script/generate content_scaffold Video duration:string
28
+ exists app/models/content
29
+ exists app/controllers/content
30
+ create app/views/content/videos
31
+ exists app/views/errors
32
+ exists app/views/sublayouts
33
+ exists public/stylesheets/content
34
+ create app/views/content/videos/index.html.erb
35
+ create app/views/content/videos/show.html.erb
36
+ create app/views/content/videos/new.html.erb
37
+ create app/views/content/videos/edit.html.erb
38
+ create app/controllers/content/videos_controller.rb
39
+ route map.resources :videos
40
+ create app/models/content/video.rb
41
+
42
+ 2. Edit the model (app/models/content/video.rb) and add fields
43
+
44
+ You can use the field method or fields method. You can optionally specify the type of the field as the second parameter to field. The following methods are available: field_name, fieldname=, fieldname_changed?, fieldname_change, fieldname_was.
45
+
46
+ class Content::Video < Content::Item
47
+ field :duration
48
+ end
49
+
50
+ 3. Add associations (has_many, has_one, belongs_to)
51
+
52
+ [has_many] creates a one-to-many association implemented as an array of ID’s. The following methods are available: singular_association_ids, singular_association_ids=, association, association=.
53
+ [has_one] creates a one-to-one association implemented as the ID of the other item. The following methods are available: association_id, association_id=, association, association=.
54
+ [belongs_to] creates a many-to-one association implemented as the ID of the other item. The following methods are available: association_id, association_id=, association, association=.
55
+
56
+ class Content::Video < Content::Item
57
+ field :heading
58
+ field :duration
59
+ belongs_to :parent_section
60
+ end
61
+
62
+ 4. Add validations. All of the standard ActiveRecord validations are available.
63
+
64
+ class Content::Video < Content::Item
65
+ field :heading
66
+ field :duration
67
+ belongs_to :parent_section
68
+ validates_presence_of :heading
69
+ end
70
+
71
+ 5. Set any indexes required.
72
+
73
+ class Content::Video < Content::Item
74
+ field :heading
75
+ field :duration
76
+ belongs_to :parent_section
77
+ validates_presence_of :heading
78
+ index :heading, :lexical
79
+ end
80
+
81
+ 6. Edit the routes file to fix the default route created. In other words:
82
+
83
+ ActionController::Routing::Routes.draw do |map|
84
+ map.resources :videos # << note it is misplaced
85
+
86
+ # Content =======================================================
87
+ map.namespace :content do |content|
88
+ # Core CMS
89
+ content.resources :sublayouts, :only => [:index, :show]
90
+ content.resources :components, :only => [:index, :show]
91
+ content.resources :templates
92
+
93
+ # Extensions
94
+ content.resources :articles
95
+ content.resources :photo_galleries
96
+ content.resources :photos
97
+ end
98
+ # END Content
99
+
100
+ ...
101
+
102
+ becomes:
103
+
104
+ ActionController::Routing::Routes.draw do |map|
105
+
106
+ # Content =======================================================
107
+ map.namespace :content do |content|
108
+ # Core CMS
109
+ content.resources :sublayouts, :only => [:index, :show]
110
+ content.resources :components, :only => [:index, :show]
111
+ content.resources :templates
112
+
113
+ # Extensions
114
+ content.resources :videos # << it should go here
115
+ content.resources :articles
116
+ content.resources :photo_galleries
117
+ content.resources :photos
118
+ end
119
+ # END Content
120
+
121
+ ...
122
+
123
+ 7. Edit your controller and views as for any normal Rails app. The controller should typically redirect to the index action in the create and update actions instead of to the show action. In other words:
124
+
125
+ format.html { redirect_to(@video) }
126
+
127
+ becomes
128
+
129
+ format.html { redirect_to(content_videos_url) }
130
+
131
+ == Using Content Items
132
+
133
+ Once you have created your Content::Item, you can use most of the standard ActiveRecord finder methods:
134
+
135
+ section = Content::Section.new()
136
+ section.heading = "My Heading"
137
+ section.body = "My Body"
138
+ section.new_record? # => true
139
+ section.save
140
+ section.new_record? # => false
141
+
142
+ section = Content::Section.new(:heading => "My Heading", :body => "My Body")
143
+ section.save
144
+
145
+ section = Content::Section.new() do |sect|
146
+ sect.heading = "My Heading"
147
+ sect.body = "My Body"
148
+ end
149
+ section.save
150
+
151
+ Content::Section.find_by_id(id)
152
+ Content::Section.find id
153
+
154
+ Content::Section.first
155
+ Content::Section.find(:first)
156
+ Content::Section.find(:first, :conditions => { :user_name => user_name })
157
+ Content::Section.find(:first, :order => :created_on, :offset => 5)
158
+
159
+ Content::Section.last
160
+ Content::Section.find(:last)
161
+ Content::Section.find(:last, :conditions => { :user_name => user_name })
162
+ Content::Section.find(:last, :order => :created_on, :offset => 5)
163
+
164
+ Content::Section.all
165
+ Content::Section.find(:all)
166
+ Content::Section.find(:all, :conditions => { :friends => "Bob" })
167
+ Content::Section.find(:all, :offset => 10, :limit => 10)
168
+
169
+ Content::Section.find_by_url "/url"
170
+ Content::Section.find_by_url_and_status "/url", "active"
171
+
172
+ == Creating a Sublayout
173
+
174
+ A sublayout is a standard Rails layout file, except it is created in the apps/views/sublayouts folder instead of apps/views/layouts. The sublayout accesses the contents of the containers as specified in the Template by yielding the container name. For example, to insert the contents of the "left" container, simply yield :left. Note that yield without an argument is the same as yield :contents as it accesses the "contents" container.
175
+
176
+ == Creating a Template
177
+
178
+ A Template joins a Sublayout together with the contents if the Sublayout’s containers. This allows the contents of the containers to be dynamically changed without changing the code of the Sublayout. A Template is just a Content::Item which has two interesting fields: heading (the name of the Template), sublayout (the relative path of the sublayout within the app/views/sublayouts folder). In addition to these standard fields, fields named after the containers in the sublayout are also created. For example, if the sublayout has the following containers [:left, :contents, :right], then there will be "left", "contents" and "right" fields in the Template. The value of each field is an array of the components for that container.
179
+
180
+ == Creating a Component
181
+
182
+ 1. Create the component_scaffold.
183
+
184
+ $ script/generate component_scaffold VideoPlayer
185
+ exists app/models/components
186
+ exists app/controllers/components
187
+ create app/views/components/video_players
188
+ exists public/stylesheets/components
189
+ create app/views/components/video_players/index.html.erb
190
+ create app/views/components/video_players/show.html.erb
191
+ create app/views/components/video_players/new.html.erb
192
+ create app/views/components/video_players/edit.html.erb
193
+ create public/stylesheets/components/video_player.css
194
+ create app/controllers/components/video_players_controller.rb
195
+ route map.resources :video_players
196
+ create app/models/components/video_player.rb
197
+
198
+ 2. Edit the model (app/models/components/video_player.rb).
199
+
200
+ class Components::VideoPlayer < Content::Item
201
+ end
202
+
203
+ 3. As you can see from above, the component is simply another Content::Item, so all of the capabilities covered in creating a Content::Item apply.
204
+
205
+ 4. Edit the routes file to fix the default route created. In other words:
206
+
207
+ ActionController::Routing::Routes.draw do |map|
208
+ map.resources :video_players # << note it is misplaced
209
+
210
+ # Components ===================================================
211
+ map.namespace :components do |component|
212
+ # Core CMS
213
+ component.resources :containers, :only => [:show]
214
+
215
+ # Extensions
216
+ component.resources :section_heros
217
+ end
218
+ # END Components
219
+ ...
220
+
221
+ becomes:
222
+
223
+ ActionController::Routing::Routes.draw do |map|
224
+ # Components ===================================================
225
+ map.namespace :components do |component|
226
+ # Core CMS
227
+ component.resources :containers, :only => [:show]
228
+
229
+ # Extensions
230
+ component.resources :section_heros
231
+ component.resources :video_players # << it goes here
232
+ # END Components
233
+ ...
234
+
235
+ 5. Edit your controller and views as for any normal Rails app. The controller should typically redirect to the index action in the create and update actions instead of to the show action. In other words:
236
+
237
+ format.html { redirect_to(@video_player) }
238
+
239
+ becomes
240
+
241
+ format.html { redirect_to(components_video_player_url) }
242
+
243
+ 6. The show view is the view that will be embedded within pages. The standard component layout (app/layouts/components.html.erb) will wrap the component in a <div> with the "component" class and a class with the name of the component model (in this case "video_player"), as follows:
244
+
245
+ <div id="video_player_6253" class="video_player component">
246
+ <!-- component body goes here -->
247
+ </div>
248
+
249
+ This allows for easy styling of the specific component and for all components.
250
+
251
+ 7. Edit the component stylesheet public/stylesheets/components/video_player.css. Put any styles specific to this component in this stylesheet. Don't worry if you think this step will create a problem at runtime, as all of these stylesheets are combined at production time into a single stylesheet.
252
+
253
+ .video_player {}
4
254
 
5
255
  == Copyright
6
256
 
7
257
  Copyright (c) 2009 Seth Yates
258
+
8
259
  Copyright (c) 2009 Independent Digital Media Pty Ltd
260
+
9
261
  See LICENSE for details.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.0
1
+ 1.0.0
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{content_manager}
5
- s.version = "0.4.0"
5
+ s.version = "1.0.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Seth Yates"]
9
- s.date = %q{2009-06-28}
9
+ s.date = %q{2009-06-30}
10
10
  s.email = %q{syates@grandcentralmedia.com.au}
11
11
  s.extra_rdoc_files = [
12
12
  "LICENSE",
@@ -20,7 +20,43 @@ Gem::Specification.new do |s|
20
20
  "Rakefile",
21
21
  "VERSION",
22
22
  "content_manager.gemspec",
23
+ "doc/created.rid",
24
+ "doc/files/README_rdoc.html",
25
+ "doc/fr_class_index.html",
26
+ "doc/fr_file_index.html",
27
+ "doc/fr_method_index.html",
28
+ "doc/index.html",
29
+ "doc/rdoc-style.css",
30
+ "generators/component_scaffold/USAGE",
31
+ "generators/component_scaffold/component_scaffold_generator.rb",
32
+ "generators/component_scaffold/templates/controller.rb",
33
+ "generators/component_scaffold/templates/model.rb",
34
+ "generators/component_scaffold/templates/style.css",
35
+ "generators/component_scaffold/templates/view_edit.html.erb",
36
+ "generators/component_scaffold/templates/view_index.html.erb",
37
+ "generators/component_scaffold/templates/view_new.html.erb",
38
+ "generators/component_scaffold/templates/view_show.html.erb",
39
+ "generators/content_scaffold/USAGE",
40
+ "generators/content_scaffold/content_scaffold_generator.rb",
41
+ "generators/content_scaffold/templates/controller.rb",
42
+ "generators/content_scaffold/templates/model.rb",
43
+ "generators/content_scaffold/templates/view_edit.html.erb",
44
+ "generators/content_scaffold/templates/view_index.html.erb",
45
+ "generators/content_scaffold/templates/view_new.html.erb",
46
+ "generators/content_scaffold/templates/view_show.html.erb",
23
47
  "init.rb",
48
+ "lib/component.rb",
49
+ "lib/content/adapters/base.rb",
50
+ "lib/content/adapters/cabinet_adapter.rb",
51
+ "lib/content/adapters/tyrant_adapter.rb",
52
+ "lib/content/item.rb",
53
+ "lib/content/item_association_class_methods.rb",
54
+ "lib/content/item_class_methods.rb",
55
+ "lib/content/item_dirty_methods.rb",
56
+ "lib/content/item_finder_class_methods.rb",
57
+ "lib/content/manager.rb",
58
+ "lib/content/sublayout.rb",
59
+ "lib/content/template.rb",
24
60
  "lib/content_manager.rb",
25
61
  "test/content_manager_test.rb",
26
62
  "test/test_helper.rb"
@@ -0,0 +1 @@
1
+ Tue, 30 Jun 2009 21:15:33 +1000
@@ -0,0 +1,487 @@
1
+ <?xml version="1.0" encoding="iso-8859-1"?>
2
+ <!DOCTYPE html
3
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
4
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
5
+
6
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
7
+ <head>
8
+ <title>File: README.rdoc</title>
9
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
10
+ <meta http-equiv="Content-Script-Type" content="text/javascript" />
11
+ <link rel="stylesheet" href=".././rdoc-style.css" type="text/css" media="screen" />
12
+ <script type="text/javascript">
13
+ // <![CDATA[
14
+
15
+ function popupCode( url ) {
16
+ window.open(url, "Code", "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400")
17
+ }
18
+
19
+ function toggleCode( id ) {
20
+ if ( document.getElementById )
21
+ elem = document.getElementById( id );
22
+ else if ( document.all )
23
+ elem = eval( "document.all." + id );
24
+ else
25
+ return false;
26
+
27
+ elemStyle = elem.style;
28
+
29
+ if ( elemStyle.display != "block" ) {
30
+ elemStyle.display = "block"
31
+ } else {
32
+ elemStyle.display = "none"
33
+ }
34
+
35
+ return true;
36
+ }
37
+
38
+ // Make codeblocks hidden by default
39
+ document.writeln( "<style type=\"text/css\">div.method-source-code { display: none }</style>" )
40
+
41
+ // ]]>
42
+ </script>
43
+
44
+ </head>
45
+ <body>
46
+
47
+
48
+
49
+ <div id="fileHeader">
50
+ <h1>README.rdoc</h1>
51
+ <table class="header-table">
52
+ <tr class="top-aligned-row">
53
+ <td><strong>Path:</strong></td>
54
+ <td>README.rdoc
55
+ </td>
56
+ </tr>
57
+ <tr class="top-aligned-row">
58
+ <td><strong>Last Update:</strong></td>
59
+ <td>Tue Jun 30 21:15:30 +1000 2009</td>
60
+ </tr>
61
+ </table>
62
+ </div>
63
+ <!-- banner header -->
64
+
65
+ <div id="bodyContent">
66
+
67
+
68
+
69
+ <div id="contextContent">
70
+
71
+ <div id="description">
72
+ <h1>Content::Manager</h1>
73
+ <h2>Overview</h2>
74
+ <p>
75
+ The Content::Manager plugin provides Content Management capability for
76
+ Rails applications. Content::Manager is built on top of a Tokyo Tyrant (or
77
+ Tokyo Cabinet) schema-less table database. Using standard Rails
78
+ functionality of models, views and controllers with some additional
79
+ &quot;glue&quot;, Content::Manager makes it straight-forward to build
80
+ component-based, content-managed Rails applications.
81
+ </p>
82
+ <h2>How it works</h2>
83
+ <p>
84
+ The Content Manager is invoked by the final route in the routes file:
85
+ </p>
86
+ <pre>
87
+ map.content_item '*content_item_url', :controller =&gt; 'content', :action =&gt; 'show'
88
+ </pre>
89
+ <p>
90
+ This route tells Rails to pass any URL (in the content_item_url parameter)
91
+ to the show action of the ContentController.
92
+ </p>
93
+ <p>
94
+ When the show action in the ContentController is invoked (inherited from
95
+ Content::Manager), the before_filter :current_content_item in the
96
+ ApplicationController is invoked first. This causes the Content::Item
97
+ specified by the URL in content_item_url to be loaded using
98
+ Content::Item.find_by_url params[:content_item_url].
99
+ </p>
100
+ <p>
101
+ Back in the ContentController#show action, the current_content_item is
102
+ checked if it is nil, if it is missing a template or if its template is
103
+ missing a sublayout. In any of these cases, a 404 Not Found error is
104
+ rendered (specified in app/views/errors/error404.html.erb).
105
+ </p>
106
+ <p>
107
+ Assuming we have a valid Content::Item with a template and a sublayout,
108
+ then the containers are rendered (using prerender_containers, content_for,
109
+ render_container and render_component in Content::Manager). The contents of
110
+ each container are stored for later use by yield within the Sublayout.
111
+ Finally, the Sublayout is rendered as a template using render :template. At
112
+ this point, no layout is specified, so Sublayouts have to provide the full
113
+ HTML. This may change in the future though.
114
+ </p>
115
+ <p>
116
+ The Content::Manager#render_component method requires special mention. This
117
+ method operates by creating a new Rack request and handing the request off
118
+ to the ActionController::Routing::Routes table. Any error from here is
119
+ raised as a RuntimeError with the message being the full HTML returned from
120
+ the error. In the case of Content::Manager, any errors from the components
121
+ is returned as the page error.
122
+ </p>
123
+ <h2>Creating a Content Item</h2>
124
+ <ol>
125
+ <li>Create the content_scaffold.
126
+
127
+ <pre>
128
+ $ script/generate content_scaffold Video duration:string
129
+ exists app/models/content
130
+ exists app/controllers/content
131
+ create app/views/content/videos
132
+ exists app/views/errors
133
+ exists app/views/sublayouts
134
+ exists public/stylesheets/content
135
+ create app/views/content/videos/index.html.erb
136
+ create app/views/content/videos/show.html.erb
137
+ create app/views/content/videos/new.html.erb
138
+ create app/views/content/videos/edit.html.erb
139
+ create app/controllers/content/videos_controller.rb
140
+ route map.resources :videos
141
+ create app/models/content/video.rb
142
+ </pre>
143
+ </li>
144
+ <li>Edit the model (app/models/content/video.rb) and add fields
145
+
146
+ <p>
147
+ You can use the field method or fields method. You can optionally specify
148
+ the type of the field as the second parameter to field. The following
149
+ methods are available: field_name, fieldname=, fieldname_changed?,
150
+ fieldname_change, fieldname_was.
151
+ </p>
152
+ <pre>
153
+ class Content::Video &lt; Content::Item
154
+ field :duration
155
+ end
156
+ </pre>
157
+ </li>
158
+ <li>Add associations (has_many, has_one, belongs_to)
159
+
160
+ <dl>
161
+ <dt>has_many</dt><dd>creates a one-to-many association implemented as an array of ID’s. The
162
+ following methods are available: singular_association_ids,
163
+ singular_association_ids=, association, association=.
164
+
165
+ </dd>
166
+ <dt>has_one</dt><dd>creates a one-to-one association implemented as the ID of the other item.
167
+ The following methods are available: association_id, association_id=,
168
+ association, association=.
169
+
170
+ </dd>
171
+ <dt>belongs_to</dt><dd>creates a many-to-one association implemented as the ID of the other item.
172
+ The following methods are available: association_id, association_id=,
173
+ association, association=.
174
+
175
+ </dd>
176
+ </dl>
177
+ <pre>
178
+ class Content::Video &lt; Content::Item
179
+ field :heading
180
+ field :duration
181
+ belongs_to :parent_section
182
+ end
183
+ </pre>
184
+ </li>
185
+ <li>Add validations. All of the standard ActiveRecord validations are
186
+ available.
187
+
188
+ <pre>
189
+ class Content::Video &lt; Content::Item
190
+ field :heading
191
+ field :duration
192
+ belongs_to :parent_section
193
+ validates_presence_of :heading
194
+ end
195
+ </pre>
196
+ </li>
197
+ <li>Set any indexes required.
198
+
199
+ <pre>
200
+ class Content::Video &lt; Content::Item
201
+ field :heading
202
+ field :duration
203
+ belongs_to :parent_section
204
+ validates_presence_of :heading
205
+ index :heading, :lexical
206
+ end
207
+ </pre>
208
+ </li>
209
+ <li>Edit the routes file to fix the default route created. In other words:
210
+
211
+ <pre>
212
+ ActionController::Routing::Routes.draw do |map|
213
+ map.resources :videos # &lt;&lt; note it is misplaced
214
+
215
+ # Content =======================================================
216
+ map.namespace :content do |content|
217
+ # Core CMS
218
+ content.resources :sublayouts, :only =&gt; [:index, :show]
219
+ content.resources :components, :only =&gt; [:index, :show]
220
+ content.resources :templates
221
+
222
+ # Extensions
223
+ content.resources :articles
224
+ content.resources :photo_galleries
225
+ content.resources :photos
226
+ end
227
+ # END Content
228
+
229
+ ...
230
+ </pre>
231
+ <p>
232
+ becomes:
233
+ </p>
234
+ <pre>
235
+ ActionController::Routing::Routes.draw do |map|
236
+
237
+ # Content =======================================================
238
+ map.namespace :content do |content|
239
+ # Core CMS
240
+ content.resources :sublayouts, :only =&gt; [:index, :show]
241
+ content.resources :components, :only =&gt; [:index, :show]
242
+ content.resources :templates
243
+
244
+ # Extensions
245
+ content.resources :videos # &lt;&lt; it should go here
246
+ content.resources :articles
247
+ content.resources :photo_galleries
248
+ content.resources :photos
249
+ end
250
+ # END Content
251
+
252
+ ...
253
+ </pre>
254
+ </li>
255
+ <li>Edit your controller and views as for any normal Rails app. The controller
256
+ should typically redirect to the index action in the create and update
257
+ actions instead of to the show action. In other words:
258
+
259
+ <pre>
260
+ format.html { redirect_to(@video) }
261
+ </pre>
262
+ <p>
263
+ becomes
264
+ </p>
265
+ <pre>
266
+ format.html { redirect_to(content_videos_url) }
267
+ </pre>
268
+ </li>
269
+ </ol>
270
+ <h2>Using Content Items</h2>
271
+ <p>
272
+ Once you have created your Content::Item, you can use most of the standard
273
+ ActiveRecord finder methods:
274
+ </p>
275
+ <pre>
276
+ section = Content::Section.new()
277
+ section.heading = &quot;My Heading&quot;
278
+ section.body = &quot;My Body&quot;
279
+ section.new_record? # =&gt; true
280
+ section.save
281
+ section.new_record? # =&gt; false
282
+
283
+ section = Content::Section.new(:heading =&gt; &quot;My Heading&quot;, :body =&gt; &quot;My Body&quot;)
284
+ section.save
285
+
286
+ section = Content::Section.new() do |sect|
287
+ sect.heading = &quot;My Heading&quot;
288
+ sect.body = &quot;My Body&quot;
289
+ end
290
+ section.save
291
+
292
+ Content::Section.find_by_id(id)
293
+ Content::Section.find id
294
+
295
+ Content::Section.first
296
+ Content::Section.find(:first)
297
+ Content::Section.find(:first, :conditions =&gt; { :user_name =&gt; user_name })
298
+ Content::Section.find(:first, :order =&gt; :created_on, :offset =&gt; 5)
299
+
300
+ Content::Section.last
301
+ Content::Section.find(:last)
302
+ Content::Section.find(:last, :conditions =&gt; { :user_name =&gt; user_name })
303
+ Content::Section.find(:last, :order =&gt; :created_on, :offset =&gt; 5)
304
+
305
+ Content::Section.all
306
+ Content::Section.find(:all)
307
+ Content::Section.find(:all, :conditions =&gt; { :friends =&gt; &quot;Bob&quot; })
308
+ Content::Section.find(:all, :offset =&gt; 10, :limit =&gt; 10)
309
+
310
+ Content::Section.find_by_url &quot;/url&quot;
311
+ Content::Section.find_by_url_and_status &quot;/url&quot;, &quot;active&quot;
312
+ </pre>
313
+ <h2>Creating a Sublayout</h2>
314
+ <p>
315
+ A sublayout is a standard Rails layout file, except it is created in the
316
+ apps/views/sublayouts folder instead of apps/views/layouts. The sublayout
317
+ accesses the contents of the containers as specified in the Template by
318
+ yielding the container name. For example, to insert the contents of the
319
+ &quot;left&quot; container, simply yield :left. Note that yield without an
320
+ argument is the same as yield :contents as it accesses the
321
+ &quot;contents&quot; container.
322
+ </p>
323
+ <h2>Creating a Template</h2>
324
+ <p>
325
+ A Template joins a Sublayout together with the contents if the
326
+ Sublayout’s containers. This allows the contents of the containers to be
327
+ dynamically changed without changing the code of the Sublayout. A Template
328
+ is just a Content::Item which has two interesting fields: heading (the name
329
+ of the Template), sublayout (the relative path of the sublayout within the
330
+ app/views/sublayouts folder). In addition to these standard fields, fields
331
+ named after the containers in the sublayout are also created. For example,
332
+ if the sublayout has the following containers [:left, :contents, :right],
333
+ then there will be &quot;left&quot;, &quot;contents&quot; and
334
+ &quot;right&quot; fields in the Template. The value of each field is an
335
+ array of the components for that container.
336
+ </p>
337
+ <h2>Creating a Component</h2>
338
+ <ol>
339
+ <li>Create the component_scaffold.
340
+
341
+ <pre>
342
+ $ script/generate component_scaffold VideoPlayer
343
+ exists app/models/components
344
+ exists app/controllers/components
345
+ create app/views/components/video_players
346
+ exists public/stylesheets/components
347
+ create app/views/components/video_players/index.html.erb
348
+ create app/views/components/video_players/show.html.erb
349
+ create app/views/components/video_players/new.html.erb
350
+ create app/views/components/video_players/edit.html.erb
351
+ create public/stylesheets/components/video_player.css
352
+ create app/controllers/components/video_players_controller.rb
353
+ route map.resources :video_players
354
+ create app/models/components/video_player.rb
355
+ </pre>
356
+ </li>
357
+ <li>Edit the model (app/models/components/video_player.rb).
358
+
359
+ <pre>
360
+ class Components::VideoPlayer &lt; Content::Item
361
+ end
362
+ </pre>
363
+ </li>
364
+ <li>As you can see from above, the component is simply another Content::Item,
365
+ so all of the capabilities covered in creating a Content::Item apply.
366
+
367
+ </li>
368
+ <li>Edit the routes file to fix the default route created. In other words:
369
+
370
+ <pre>
371
+ ActionController::Routing::Routes.draw do |map|
372
+ map.resources :video_players # &lt;&lt; note it is misplaced
373
+
374
+ # Components ===================================================
375
+ map.namespace :components do |component|
376
+ # Core CMS
377
+ component.resources :containers, :only =&gt; [:show]
378
+
379
+ # Extensions
380
+ component.resources :section_heros
381
+ end
382
+ # END Components
383
+ ...
384
+ </pre>
385
+ <p>
386
+ becomes:
387
+ </p>
388
+ <pre>
389
+ ActionController::Routing::Routes.draw do |map|
390
+ # Components ===================================================
391
+ map.namespace :components do |component|
392
+ # Core CMS
393
+ component.resources :containers, :only =&gt; [:show]
394
+
395
+ # Extensions
396
+ component.resources :section_heros
397
+ component.resources :video_players # &lt;&lt; it goes here
398
+ # END Components
399
+ ...
400
+ </pre>
401
+ </li>
402
+ <li>Edit your controller and views as for any normal Rails app. The controller
403
+ should typically redirect to the index action in the create and update
404
+ actions instead of to the show action. In other words:
405
+
406
+ <pre>
407
+ format.html { redirect_to(@video_player) }
408
+ </pre>
409
+ <p>
410
+ becomes
411
+ </p>
412
+ <pre>
413
+ format.html { redirect_to(components_video_player_url) }
414
+ </pre>
415
+ </li>
416
+ <li>The show view is the view that will be embedded within pages. The standard
417
+ component layout (app/layouts/components.html.erb) will wrap the component
418
+ in a &lt;div&gt; with the &quot;component&quot; class and a class with the
419
+ name of the component model (in this case &quot;video_player&quot;), as
420
+ follows:
421
+
422
+ <pre>
423
+ &lt;div id=&quot;video_player_6253&quot; class=&quot;video_player component&quot;&gt;
424
+ &lt;!-- component body goes here --&gt;
425
+ &lt;/div&gt;
426
+ </pre>
427
+ </li>
428
+ </ol>
429
+ <p>
430
+ This allows for easy styling of the specific component and for all
431
+ components.
432
+ </p>
433
+ <ol>
434
+ <li>Edit the component stylesheet
435
+ public/stylesheets/components/video_player.css. Put any styles specific to
436
+ this component in this stylesheet. Don&#8216;t worry if you think this step
437
+ will create a problem at runtime, as all of these stylesheets are combined
438
+ at production time into a single stylesheet.
439
+
440
+ <pre>
441
+ .video_player {}
442
+ </pre>
443
+ </li>
444
+ </ol>
445
+ <h2>Copyright</h2>
446
+ <p>
447
+ Copyright (c) 2009 Seth Yates
448
+ </p>
449
+ <p>
450
+ Copyright (c) 2009 Independent Digital Media Pty Ltd
451
+ </p>
452
+ <p>
453
+ See LICENSE for details.
454
+ </p>
455
+
456
+ </div>
457
+
458
+
459
+ </div>
460
+
461
+
462
+ </div>
463
+
464
+
465
+ <!-- if includes -->
466
+
467
+ <div id="section">
468
+
469
+
470
+
471
+
472
+
473
+
474
+
475
+
476
+ <!-- if method_list -->
477
+
478
+
479
+ </div>
480
+
481
+
482
+ <div id="validator-badges">
483
+ <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
484
+ </div>
485
+
486
+ </body>
487
+ </html>