radiant-page_factory-extension 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/EXAMPLES.md +136 -0
  2. data/README.md +59 -0
  3. data/Rakefile +136 -0
  4. data/VERSION +1 -0
  5. data/app/controllers/admin/page_factories_controller.rb +42 -0
  6. data/app/helpers/admin/part_description_helper.rb +9 -0
  7. data/app/views/admin/page_factories/_page_factory.html.haml +4 -0
  8. data/app/views/admin/page_factories/index.haml +0 -0
  9. data/app/views/admin/page_parts/_part_description.html.haml +2 -0
  10. data/app/views/admin/pages/_add_child_column.html.haml +3 -0
  11. data/app/views/admin/pages/_edit_header.html.haml +1 -0
  12. data/app/views/admin/pages/_page_factories.html.haml +4 -0
  13. data/app/views/admin/pages/_page_factory_field.html.haml +1 -0
  14. data/config/locales/en.yml +3 -0
  15. data/config/routes.rb +5 -0
  16. data/db/migrate/20100321222140_add_page_factory.rb +9 -0
  17. data/lib/page_factory.rb +12 -0
  18. data/lib/page_factory/base.rb +81 -0
  19. data/lib/page_factory/manager.rb +120 -0
  20. data/lib/page_factory/page_extensions.rb +25 -0
  21. data/lib/page_factory/page_part_extensions.rb +9 -0
  22. data/lib/page_factory/pages_controller_extensions.rb +32 -0
  23. data/lib/tasks/page_factory_extension_tasks.rake +95 -0
  24. data/page_factory_extension.rb +25 -0
  25. data/public/javascripts/admin/dropdown.js +153 -0
  26. data/public/javascripts/admin/pagefactory.js +30 -0
  27. data/public/stylesheets/admin/page_factory.css +14 -0
  28. data/public/stylesheets/sass/admin/dropdown.sass +32 -0
  29. data/public/stylesheets/sass/modules/_gradient.sass +47 -0
  30. data/public/stylesheets/sass/modules/_rounded.sass +41 -0
  31. data/public/stylesheets/sass/modules/_shadow.sass +9 -0
  32. data/spec/controllers/admin/page_factories_controller_spec.rb +47 -0
  33. data/spec/controllers/admin/pages_controller_spec.rb +63 -0
  34. data/spec/helpers/admin/part_description_helper_spec.rb +42 -0
  35. data/spec/lib/manager_spec.rb +218 -0
  36. data/spec/lib/page_extensions_spec.rb +15 -0
  37. data/spec/models/page_factory_spec.rb +95 -0
  38. data/spec/models/page_spec.rb +27 -0
  39. data/spec/spec.opts +6 -0
  40. data/spec/spec_helper.rb +36 -0
  41. metadata +128 -0
@@ -0,0 +1,30 @@
1
+ Dropdown.PageFactoryTriggerBehavior = Behavior.create(Dropdown.TriggerBehavior, {
2
+ initialize: function($super) {
3
+ $super()
4
+ this.menu = Dropdown.Menu.findOrCreate($('add_child_dropdown'))
5
+ },
6
+ onclick: function($super, event) {
7
+ if(this.menu.wrapper.visible()) {
8
+ $super(event)
9
+ } else {
10
+ new Ajax.Request(this.element.href, {
11
+ method: 'get',
12
+ onSuccess: function(data) {
13
+ this.menu.element.innerHTML = data.responseText
14
+ var factories = this.menu.element.childElements($$('li'))
15
+ if(factories.length == 1) {
16
+ window.location = factories[0].down().href
17
+ event.stop()
18
+ } else {
19
+ $super(event)
20
+ }
21
+ }.bind(this).bind($super).bind(event)
22
+ })
23
+ }
24
+ event.stop()
25
+ }
26
+ })
27
+
28
+ Event.addBehavior({
29
+ 'a.dropdown': Dropdown.PageFactoryTriggerBehavior()
30
+ })
@@ -0,0 +1,14 @@
1
+ div.popup .factory_description {
2
+ border-bottom: 1px dotted silver;
3
+ font-size: 95%;
4
+ padding: 12px 0; }
5
+ div.popup .factory_description h4 {
6
+ color: green;
7
+ font-size: 120%;
8
+ margin-top: 0px;
9
+ margin-bottom: 8px; }
10
+ span.part_description {
11
+ display: block;
12
+ font-size: 80%;
13
+ margin-top: 8px;
14
+ }
@@ -0,0 +1,32 @@
1
+ @import ../modules/shadow.sass
2
+ @import ../modules/rounded.sass
3
+ @import ../modules/gradient.sass
4
+
5
+ .dropdown_wrapper
6
+ border: 1px solid #b3b3b3
7
+ border-top: 1px solid #c3c3c3
8
+ +box-shadow(0px, 8px, 25px, rgba(0,0,0,0.35))
9
+ +round-corner("top-right")
10
+ +round-bottom-corners
11
+
12
+ ul.menu
13
+ background: white
14
+ font-size: 80%
15
+ padding: 5px 20px
16
+ +round-corner("top-right")
17
+ +round-bottom-corners
18
+ li
19
+ margin: 0 -20px
20
+ &.separator
21
+ margin: 5px -18px
22
+ border-top: 1px solid #e5e5e5
23
+ a
24
+ color: black
25
+ display: block
26
+ padding: 6px 20px
27
+ text-decoration: none
28
+ border-top: 1px solid white
29
+ &:hover
30
+ background: #e0f0ff url(../../images/admin/menu_highlight.png) top center repeat-x
31
+ border-top: 1px solid #d5f0ff
32
+ +v-gradient(#e5f5ff, #b5d0f5)
@@ -0,0 +1,47 @@
1
+ // Originally based on the version in compass: http://github.com/chriseppstein/compass
2
+
3
+ =gradient(!type, !coords, !color_start, !color_end, !color_stop = false)
4
+ !gradient= "#{!coords}, from(#{!color_start}), to(#{!color_end})"
5
+ @if !color_stop
6
+ background: gradient(#{!type}, #{!gradient}, #{!color_stop})
7
+ background: -webkit-gradient(#{!type}, #{!gradient})
8
+ background: -webkit-gradient(#{!type}, #{!gradient}, #{!color_stop})
9
+ background: -moz-#{!type}-gradient(#{!gradient}, #{!color_stop})
10
+ @else
11
+ background: gradient(#{!type}, #{!gradient})
12
+ background: -webkit-gradient(#{!type}, #{!gradient})
13
+ background: -moz-#{!type}-gradient(#{!gradient})
14
+
15
+ //*
16
+ // This will yeild a radial gradient with an apparent specular highlight
17
+ +radial-gradient("45 45, 10, 52 50, 30", Cyan, DodgerBlue)
18
+
19
+ =radial-gradient(!coords, !color1, !color2, !color_stop = false)
20
+ +gradient("radial", !coords, !color1, !color2, !color_stop)
21
+
22
+ //*
23
+ // This yields a linear gradient spanning from !start to !end coordinates
24
+ +linear-gradient("left top", "left bottom", #fff, #ddd)
25
+
26
+ =linear-gradient(!start, !end, !color1, !color2, !color_stop = false)
27
+ !coords = !start + ", " + !end
28
+ +gradient("linear", !coords, !color1, !color2, !color_stop)
29
+
30
+ //*
31
+ // This yields a gradient starting at the top with #fff, ending in #aaa
32
+ +v-gradient(#fff, #aaa)
33
+ // Same as above but with a #ccc at the halfway point
34
+ +v-gradient(#fff, #aaa, color_stop(50%, #ccc))
35
+ // Same as the first example but with #ccc at the 30% from the top, and #bbb at 70% from the top
36
+ +v-gradient(#fff, #aaa, color_stop(30%, #ccc, 70%, #bbb))
37
+
38
+ =v-gradient(!color1, !color2, !color_stop = false)
39
+ +linear-gradient("left top", "left bottom", !color1, !color2, !color_stop)
40
+
41
+ //*
42
+ // This yields a horizontal linear gradient spanning from left to right
43
+ // It can be used just like v-gradient above
44
+ h-gradient(#fff, #ddd)
45
+
46
+ =h-gradient(!color1, !color2, !color_stop = false)
47
+ +linear-gradient("left top", "right top", !color1, !color2, !color_stop)
@@ -0,0 +1,41 @@
1
+ !default_rounded_amount ||= 5px
2
+
3
+ // Round corner at position by amount.
4
+ // values for position: "top-left", "top-right", "bottom-left", "bottom-right"
5
+ =round-corner(!position, !amount = !default_rounded_amount)
6
+ border-#{!position}-radius= !amount
7
+ @if !position == "top-left"
8
+ -moz-border-radius-topleft= !amount
9
+ @if !position == "top-right"
10
+ -moz-border-radius-topright= !amount
11
+ @if !position == "bottom-left"
12
+ -moz-border-radius-bottomleft= !amount
13
+ @if !position == "bottom-right"
14
+ -moz-border-radius-bottomright= !amount
15
+ -webkit-border-#{!position}-radius= !amount
16
+
17
+ // Round left corners by amount
18
+ =round-left-corners(!amount = !default_rounded_amount)
19
+ +round-corner("top-left", !amount)
20
+ +round-corner("bottom-left", !amount)
21
+
22
+ // Round right corners by amount
23
+ =round-right-corners(!amount = !default_rounded_amount)
24
+ +round-corner("top-right", !amount)
25
+ +round-corner("bottom-right", !amount)
26
+
27
+ // Round top corners by amount
28
+ =round-top-corners(!amount = !default_rounded_amount)
29
+ +round-corner("top-left", !amount)
30
+ +round-corner("top-right", !amount)
31
+
32
+ // Round bottom corners by amount
33
+ =round-bottom-corners(!amount = !default_rounded_amount)
34
+ +round-corner("bottom-left", !amount)
35
+ +round-corner("bottom-right", !amount)
36
+
37
+ // Round all corners by amount
38
+ =round-corners(!amount = !default_rounded_amount)
39
+ border-radius: !amount
40
+ -moz-border-radius= !amount
41
+ -webkit-border-radius= !amount
@@ -0,0 +1,9 @@
1
+ =box-shadow(!x=1px, !y=1px, !blur=4px, !color=#666)
2
+ box-shadow= !x !y !blur !color
3
+ -webkit-box-shadow= !x !y !blur !color
4
+ -moz-box-shadow= !x !y !blur !color
5
+
6
+ =no-box-shadow
7
+ box-shadow= "none"
8
+ -webkit-box-shadow= "none"
9
+ -moz-box-shadow= "none"
@@ -0,0 +1,47 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe Admin::PageFactoriesController do
4
+ dataset :users, :pages
5
+
6
+ class ParamPageFactory < PageFactory::Base
7
+ end
8
+
9
+ before do
10
+ login_as :admin
11
+ end
12
+
13
+ describe "index" do
14
+ it "should expose page instance" do
15
+ get :index, :page => page_id(:home)
16
+ assigns(:page).should eql(pages(:home))
17
+ end
18
+
19
+ it "should expose factory list" do
20
+ get :index, :page => page_id(:home)
21
+ assigns(:factories).should include(PageFactory::Base)
22
+ end
23
+
24
+ end
25
+
26
+ describe ".factory_link" do
27
+ before :all do
28
+ Admin::PageFactoriesController.send :public, :factory_link
29
+ end
30
+
31
+ it "should give a link for a new page" do
32
+ controller.instance_variable_set :@page, nil
33
+ controller.factory_link.should eql(new_admin_page_path)
34
+ end
35
+
36
+ it "should give a link for an existing page" do
37
+ controller.instance_variable_set :@page, pages(:home)
38
+ controller.factory_link.should eql(new_admin_page_child_path(pages(:home)))
39
+ end
40
+
41
+ it "should create a link with a page_factory param" do
42
+ controller.instance_variable_set :@page, pages(:home)
43
+ controller.factory_link(ParamPageFactory).should eql(new_admin_page_child_path(pages(:home), :factory => 'ParamPageFactory'))
44
+ end
45
+ end
46
+
47
+ end
@@ -0,0 +1,63 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe Admin::PagesController do
4
+ dataset :config, :users, :layouts
5
+
6
+ class ControllerPageFactory < PageFactory::Base
7
+ part 'alpha'
8
+ part 'beta'
9
+ end
10
+
11
+ class ArchivePageFactory < PageFactory::Base
12
+ layout 'UTF8'
13
+ page_class 'ArchivePage'
14
+ end
15
+
16
+ before :each do
17
+ login_as :admin
18
+ end
19
+
20
+ describe "#new" do
21
+ it "should assign default parts when no factory is passed" do
22
+ get :new
23
+ assigns(:page).parts.map(&:name).should eql(%w(body extended))
24
+ end
25
+
26
+ it "should not set a page factory if none is given" do
27
+ get :new
28
+ assigns(:page).page_factory.should be_nil
29
+ end
30
+
31
+ it "should set the new page's factory" do
32
+ get :new, :factory => 'ControllerPageFactory'
33
+ assigns(:page).page_factory.should eql(ControllerPageFactory)
34
+ end
35
+
36
+ it "should set the current factory" do
37
+ PageFactory.should_receive(:current_factory=).with('ControllerPageFactory').ordered
38
+ PageFactory.should_receive(:current_factory=).with(nil).ordered
39
+
40
+ get :new, :factory => 'ControllerPageFactory'
41
+ end
42
+
43
+ it "should assign parts to @page based on the current factory" do
44
+ get :new, :factory => 'ControllerPageFactory'
45
+ assigns(:page).parts.map(&:name).should eql(%w(body extended alpha beta))
46
+ end
47
+
48
+ it "should not choke on bad factory names" do
49
+ get :new, :factory => 'BogusFactory'
50
+ response.should be_success
51
+ end
52
+
53
+ it "should pass a layout" do
54
+ get :new, :factory => 'ArchivePageFactory'
55
+ assigns(:page).layout.should eql(layouts(:utf8))
56
+ end
57
+
58
+ it "should pass a page type" do
59
+ get :new, :factory => 'ArchivePageFactory'
60
+ assigns(:page).class_name.should eql('ArchivePage')
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,42 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe Admin::PartDescriptionHelper do
4
+ include Admin::PartDescriptionHelper
5
+
6
+ class DescriptionPageFactory < PageFactory::Base
7
+ part 'normal', :description => 'sub'
8
+ end
9
+
10
+ class DescriptionPart < PagePart ; end
11
+
12
+ before do
13
+ @page = Page.new :page_factory => 'DescriptionPageFactory'
14
+ @part = PagePart.new :name => 'normal'
15
+ end
16
+
17
+ it "should get description from page factory" do
18
+ description_for(@part).should eql('sub')
19
+ end
20
+
21
+ it "should return nothing if page is unset" do
22
+ # as is the case when adding parts
23
+ @page = nil
24
+ description_for(@part).should be_blank
25
+ end
26
+
27
+ it "should return nothing if factory is unset" do
28
+ @page.page_factory = nil
29
+ description_for(@part).should be_blank
30
+ end
31
+
32
+ it "should not describe a different part class" do
33
+ part = DescriptionPart.new :name => 'normal'
34
+ description_for(part).should be_blank
35
+ end
36
+
37
+ private
38
+
39
+ def logger
40
+ double('logger').as_null_object
41
+ end
42
+ end
@@ -0,0 +1,218 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe PageFactory::Manager do
4
+ dataset do
5
+ create_record :page, :managed, :title => 'managed', :slug => 'managed', :breadcrumb => 'managed', :page_factory => 'ManagedPageFactory'
6
+ create_record :page, :plain, :title => 'plain', :slug => 'plain', :breadcrumb => 'plain'
7
+ create_record :page, :other, :title => 'other', :slug => 'other', :breadcrumb => 'other', :page_factory => 'OtherPageFactory'
8
+ create_record :page_part, :existing, :name => 'existing'
9
+ create_record :page_part, :old, :name => 'old'
10
+ create_record :page_part, :new, :name => 'new'
11
+ end
12
+
13
+ class ManagedPageFactory < PageFactory::Base
14
+ part 'existing'
15
+ part 'new'
16
+ end
17
+
18
+ class OtherPageFactory < PageFactory::Base
19
+ part 'new'
20
+ end
21
+
22
+ before do
23
+ @managed, @plain, @other, @existing, @old, @new =
24
+ pages(:managed), pages(:plain), pages(:other), page_parts(:existing), page_parts(:old), page_parts(:new)
25
+ end
26
+
27
+ describe ".prune_parts!" do
28
+ it "should remove vestigial parts" do
29
+ @managed.parts.concat [@existing, @old]
30
+ PageFactory::Manager.prune_parts!
31
+ @managed.reload.parts.should_not include(@old)
32
+ end
33
+
34
+ it "should leave listed parts alone" do
35
+ @managed.parts.concat [@existing, @old]
36
+ PageFactory::Manager.prune_parts!
37
+ @managed.reload.parts.should include(@existing)
38
+ end
39
+
40
+ it "should leave Plain Old Pages alone" do
41
+ @plain.parts.concat [@existing, @old]
42
+ PageFactory::Manager.prune_parts!
43
+ @plain.reload.parts.should include(@existing)
44
+ @plain.reload.parts.should include(@old)
45
+ end
46
+
47
+ it "should operate on a single factory" do
48
+ e, o = @existing.clone, @old.clone
49
+ @managed.parts.concat [e, o]
50
+ @other.parts.concat [@existing, @old]
51
+ PageFactory::Manager.prune_parts! ManagedPageFactory
52
+ @managed.parts.should_not include(o)
53
+ @other.reload.parts.should include(@old)
54
+ end
55
+
56
+ it "should operate on Plain Old Pages" do
57
+ PageFactory::Base.parts = [@old, @existing]
58
+ @plain.parts << extra = PagePart.new(:name => 'extra')
59
+ PageFactory::Manager.prune_parts! :PageFactory
60
+ @plain.reload.parts.should_not include(extra)
61
+ end
62
+ end
63
+
64
+ describe ".sync_parts!" do
65
+ class AddPartClass < ActiveRecord::Migration
66
+ def self.up
67
+ add_column :page_parts, :part_class, :string
68
+ PagePart.reset_column_information
69
+ end
70
+
71
+ def self.down
72
+ remove_column :page_parts, :part_class
73
+ PagePart.reset_column_information
74
+ end
75
+ end
76
+
77
+ class SubPagePart < PagePart ; end
78
+
79
+ before :all do
80
+ ActiveRecord::Migration.verbose = false
81
+ AddPartClass.up
82
+ PagePart.class_eval do
83
+ set_inheritance_column :part_class
84
+ end
85
+ end
86
+
87
+ after :all do
88
+ AddPartClass.down
89
+ PagePart.class_eval do
90
+ set_inheritance_column nil
91
+ end
92
+ end
93
+
94
+ before :each do
95
+ @part = SubPagePart.new(:name => 'new')
96
+ @managed.parts = [@part]
97
+ end
98
+
99
+ it "should delete parts whose classes don't match" do
100
+ PageFactory::Manager.sync_parts!
101
+ @managed.reload.parts.should_not include(@new)
102
+ end
103
+
104
+ it "should replace parts whose classes don't match" do
105
+ PageFactory::Manager.sync_parts!
106
+ @managed.reload.parts.detect { |p| p.name == 'new'}.class.should == PagePart
107
+ end
108
+
109
+ it "should leave synced parts alone" do
110
+ @managed.parts = [@new]
111
+ PageFactory::Manager.sync_parts!
112
+ @managed.reload.parts.should eql([@new])
113
+ end
114
+
115
+ it "should operate on a single factory" do
116
+ c = @part.clone
117
+ @other.parts = [c]
118
+ PageFactory::Manager.sync_parts! :ManagedPageFactory
119
+ @other.reload.parts.should include(c)
120
+ end
121
+
122
+ it "should operate on Plain Old Pages" do
123
+ PageFactory::Base.parts = [@new]
124
+ @plain.parts << SubPagePart.new(:name => 'new')
125
+ PageFactory::Manager.sync_parts! :PageFactory
126
+ @plain.reload.parts.detect { |p| p.name == 'new' }.class.should == PagePart
127
+ end
128
+ end
129
+
130
+ describe ".update_parts" do
131
+ it "should add missing parts" do
132
+ @managed.parts.should be_empty
133
+ PageFactory::Manager.update_parts
134
+ @managed.parts.reload.map(&:name).should include('new')
135
+ end
136
+
137
+ it "should not duplicate existing parts" do
138
+ @managed.parts.concat [@new, @existing]
139
+ lambda { PageFactory::Manager.update_parts }.should_not change(@managed.parts, :size)
140
+ end
141
+
142
+ it "should not replace matching parts" do
143
+ @managed.parts.concat [@new, @existing]
144
+ PageFactory::Manager.update_parts
145
+ @managed.reload
146
+ @managed.parts.should include(@new)
147
+ @managed.parts.should include(@existing)
148
+ end
149
+
150
+ it "should operate on a single factory" do
151
+ @other.parts = [e = @existing.clone]
152
+ PageFactory::Manager.update_parts :ManagedPageFactory
153
+ @other.reload.parts.should == [e]
154
+ end
155
+
156
+ it "should operate on Plain Old Pages" do
157
+ PageFactory::Base.parts = [@new]
158
+ @plain.parts.should be_empty
159
+ PageFactory::Manager.update_parts :PageFactory
160
+ @plain.reload.parts.map(&:name).should include('new')
161
+ end
162
+ end
163
+
164
+ describe ".sync_layouts!" do
165
+ dataset do
166
+ create_record :layout, :one, :name => 'Layout One'
167
+ create_record :layout, :two, :name => 'Layout Two'
168
+ end
169
+ ManagedPageFactory.layout 'Layout One'
170
+ OtherPageFactory.layout 'Layout One'
171
+
172
+ before do
173
+ @managed.layout = layouts(:two)
174
+ @managed.save
175
+ end
176
+
177
+ it "should change page layout to match factory" do
178
+ PageFactory::Manager.sync_layouts!
179
+ @managed.reload.layout.should eql(layouts(:one))
180
+ end
181
+
182
+ it "should operate on a single factory" do
183
+ @other.layout = layouts(:two)
184
+ @other.save
185
+ PageFactory::Manager.sync_layouts! :ManagedPageFactory
186
+ @other.reload.layout.should eql(layouts(:two))
187
+ end
188
+
189
+ it "should operate on Plain Old Pages" do
190
+ @plain.layout = layouts(:one)
191
+ @plain.save
192
+ PageFactory::Manager.sync_layouts! :PageFactory
193
+ @plain.reload.layout.should be_nil
194
+ end
195
+ end
196
+
197
+ describe ".sync_classes!" do
198
+ class SubPage < Page ; end
199
+ ManagedPageFactory.page_class 'SubPage'
200
+ OtherPageFactory.page_class 'SubPage'
201
+
202
+ it "should change page class to match factory" do
203
+ PageFactory::Manager.sync_classes!
204
+ pages(:managed).should be_kind_of(SubPage)
205
+ end
206
+
207
+ it "should operate on a single factory" do
208
+ PageFactory::Manager.sync_classes! :ManagedPageFactory
209
+ pages(:other).should be_kind_of(Page)
210
+ end
211
+
212
+ it "should operate on Plain Old Pages" do
213
+ pages(:plain).update_attribute :class_name, 'SubPage'
214
+ PageFactory::Manager.sync_classes! :PageFactory
215
+ pages(:plain).class.should == Page
216
+ end
217
+ end
218
+ end