radiant-page_factory-extension 1.0.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.
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