rooftop-rails-extras 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +24 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +621 -0
  5. data/README.md +162 -0
  6. data/Rakefile +2 -0
  7. data/app/controllers/concerns/rooftop/rails/extras/contact_form_handler.rb +46 -0
  8. data/app/helpers/rooftop/rails/extras/navigation_helper.rb +117 -0
  9. data/app/helpers/rooftop/rails/extras/related_fields_helper.rb +14 -0
  10. data/app/models/concerns/rooftop/rails/extras/contact_form.rb +51 -0
  11. data/app/models/concerns/rooftop/rails/extras/page_redirect.rb +20 -0
  12. data/app/models/concerns/rooftop/rails/extras/resolved_children.rb +21 -0
  13. data/app/models/concerns/rooftop/rails/extras/resource_search.rb +48 -0
  14. data/lib/generators/rooftop/extras_generator.rb +21 -0
  15. data/lib/generators/rooftop/page_generator.rb +14 -0
  16. data/lib/generators/rooftop/pages_controller_generator.rb +27 -0
  17. data/lib/generators/rooftop/post_generator.rb +9 -0
  18. data/lib/generators/rooftop/template_generator.rb +9 -0
  19. data/lib/generators/rooftop/templates/application.html.erb +14 -0
  20. data/lib/generators/rooftop/templates/index.html.erb +2 -0
  21. data/lib/generators/rooftop/templates/page_class.rb.erb +4 -0
  22. data/lib/generators/rooftop/templates/pages_controller.rb.erb +49 -0
  23. data/lib/generators/rooftop/templates/post_class.rb.erb +3 -0
  24. data/lib/generators/rooftop/templates/show.html.erb +3 -0
  25. data/lib/generators/rooftop/templates/template.html.erb +8 -0
  26. data/lib/rooftop/rails/extras/engine.rb +38 -0
  27. data/lib/rooftop/rails/extras/version.rb +7 -0
  28. data/lib/rooftop/rails/extras.rb +14 -0
  29. data/rooftop-rails-extras.gemspec +29 -0
  30. metadata +142 -0
data/README.md ADDED
@@ -0,0 +1,162 @@
1
+ # Rooftop::Rails::Extras
2
+ A selection of things we're finding handy at [Error](http://error.agency) so you might, too.
3
+
4
+ ## `Rooftop::Rails::Extras::ResourceSearch` - Free-text Searchable resources
5
+ Rooftop CMS search is a work in progress, and only supports basic WordPress LIKE queries at the moment. You can do a little better than that, at the expense of getting a collection of all resources, if you do a regex match in Rails.
6
+
7
+ ```
8
+ class SomePostType
9
+ include Rooftop::Post
10
+ include Rooftop::Rails::Extras::ResourceSearch #searches title and content by default
11
+
12
+ # add extra fields to search, by creating a lambda which returns text. Markup and punctuation is stripped after calling it.
13
+ add_search_field ->(record) {
14
+ record.fields.some_custom_field
15
+ }
16
+
17
+ # Or a more complicated example, where a custom field is returning an object of some sort
18
+ add_search_field ->(record) {
19
+ record.fields.some_complex_object[:some][:deep][:data]
20
+ }
21
+
22
+ # or even a collection, from an ACF repeater
23
+ add_search_field ->(record) {
24
+ content = record.fields.some_repeater.collect do |object|
25
+ object.some.deep.thing
26
+ end
27
+ content.join(" ") #return the collection, joined with a space
28
+ }
29
+
30
+ end
31
+ ```
32
+
33
+ ## Contact form handling
34
+ Almost every website we build needs a simple mail handler. Extending the [mail_form gem](https://github.com/plataformatec/mail_form), we've added a way to handle these with minimal fuss. Some assumptions are made in the name of expedience; if you need more than this, you probably need to use `mail_form` directly.
35
+
36
+ ### Create a contact form model, inheriting from the Rooftop extras one
37
+ First define a model which inherits from `Rooftop::Rails::Extras::ContactForm`. You need to do some setup in here, including:
38
+
39
+ * Defining the fields (and types) you need to handle
40
+ * The to / from addresses, and any extra headers you want to define in the email
41
+
42
+ ```
43
+ class ContactForm < Rooftop::Rails::Extras::ContactForm #you can call your class anything
44
+ #the values of these hashes are Rails form helper method names
45
+ self.fields = {
46
+ name: :text_field,
47
+ email: :text_field,
48
+ message: :text_area
49
+ }
50
+ self.to = "the email you want to send the contact messages to"
51
+ self.from = "the email from which you want to send the messages"
52
+ end
53
+ ```
54
+
55
+ ### Create a controller and add the mixin
56
+ Write a simple controller and add the `Rooftop::Rails::Extras::ContactFormHandler` mixin.
57
+
58
+ ```
59
+ class ContactFormsController < ApplicationController # you can call your controller anything to suit
60
+ include Rooftop::Rails::Extras::ContactFormHandler
61
+ # Declare the class you've just defined, above. If you called it something else, change it here.
62
+ self.contact_form = ::ContactForm
63
+ end
64
+ ```
65
+
66
+ ### Create a route
67
+ If you called your controller something else, you need to tweak the route accordingly. Of course, this is all just normal Rails routes so you can do what you need to, as long as a POST request hits your controller.
68
+
69
+ ```
70
+ resources :contact_forms, only: :create
71
+ ```
72
+
73
+ ### Define a 'thankyou' page in Rooftop CMS
74
+ When the form is submitted, the controller redirects the user to a thankyou page, which is any page you want. You need to derive the ID of this page somehow, to get it into a hidden field in the form. Generally, we create a field in Rooftop for this, and decorate the Page model with a'thankyou_page_id' method which returns the ID.
75
+
76
+ ### Create your form
77
+ How you build the form is up to you, but here's a quick way to use the fields you defined above.
78
+
79
+ ```
80
+ <%= form_for ContactForm.new, method: :post, class: 'contact' do |f| %>
81
+ <!-- The form will create a flash with the errors from the form, if any -->
82
+ <% if flash[:notice].present? %>
83
+ <p class="form-errors">
84
+ Your message couldn't be sent: <%= flash[:notice] %>
85
+ </p>
86
+ <% end %>
87
+ <!-- f.object is the ContactForm instance above, so we know it responds to `fields` -->
88
+ <% f.object.fields.each do |field, type| %>
89
+ <!-- iterate over each field, setting the value if passed in, and adding an error class if there's an error on the field -->
90
+ <p>
91
+ <%= f.label field %>
92
+ <%= f.send(
93
+ type,
94
+ field,
95
+ class: (params[:errors].present? && params[:errors].include?(field.to_s)) ? "has-errors" : "",
96
+ value: params[field],
97
+ required: true
98
+ )%>
99
+ </p>
100
+ <% end %>
101
+ <!-- these hidden tags tell the controller how to redirect the user. If there's an error, we'll redirect back to this page, with get params for the errors and each completed field. If there isn't, the user is redirected to the thankyou page -->
102
+ <%= hidden_field_tag :from_page, page.id %>
103
+ <%= hidden_field_tag :to_page, page.thankyou_page_id %>
104
+
105
+ <p><input type="submit" id="submit" value="Send" /></p>
106
+ <% end %>
107
+ ```
108
+
109
+ ### Handling Errors
110
+ `Rooftop::Rails::Extras::ContactForm` assumes every field you enter is required. If there are errors on the model, the controller will return the user to the original page (as a redirect) but add some querystring params:
111
+
112
+ * `errors[]` - an array of each fieldname with errors (in our example above, we check for this and add a classname on the offending inputs
113
+ * params for each entered field, so you can set the field values to save data re-entry
114
+
115
+ ## Navigation View Helpers
116
+ For nested post types (pages, products etc.) you might well want to create subnavigation, showing the path to this page, its siblings and parent pages. We use this often enough at [Error Agency](http://error.agency) to have a helper do the iteration.
117
+
118
+ We also generally use WordPress menus in Rooftop for the main navigation on a site, and also footer links.
119
+
120
+ ### `subnavigation_for()` helper
121
+ Given a page, this helper will return a nested unordered list of links, starting at the root of the page, and rendering the page, it's parents and their siblings, and its ancestors above parents.
122
+
123
+ Here's a call to the helper in a view, with the default options shown:
124
+
125
+ ```
126
+ <%# @page is defined in your controller as a call to Page.find() %>
127
+ <%= subnavigation_for(@page, class: 'subnavigation-list', current_class: 'is-current', current: nil) %>
128
+ ```
129
+
130
+ If you want to specify different class names for the UL and currently-active page, just amend the options.
131
+
132
+ If you want to highlight *another page* when the subnav is rendered, that's fine too:
133
+
134
+ ```
135
+ <%= subnavigation_for(@page, current: @another_page) %>
136
+ ```
137
+
138
+ You might want to do this if, for example, your markup requires some navigational components in 2 different places.
139
+
140
+ ### `menu_for()` helper
141
+ Given a `Rooftop::Menus::Menu` object, `menu_for()` will return a nested unordered list.
142
+
143
+ ```
144
+ <%# @menu is defined in your controller as a call to Rooftop::Menus::Menu.find()
145
+ <%= menu_for(@menu, current_class: 'is-current') %>
146
+ ```
147
+
148
+ A utility `menu-level-[x]` class is added to each level of the nested menu, in case you want to target a particular level in a different way with css. Useful for dropdown menus.
149
+
150
+ ### `breadcrumbs_for()` helper
151
+ Breadcrumbs are similar to a nested subnavigation, but they only represent a direct path to this page (or other nested post type).
152
+
153
+ ```
154
+ <%# @page is defined in your controller as a call to Page.find() %>
155
+ <%= breadcrumbs_for(@page, class: 'breadcrumbs', current_class: 'is-current') %>
156
+ ```
157
+
158
+ # Licence
159
+ This gem is licenced GPLv3; contributions are most welcome!
160
+
161
+ # Contributing
162
+ The usual: fork, change, PR. Clean PRs (rebased if necessary) are preferred, but anything you can do is valuable.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,46 @@
1
+ module Rooftop
2
+ module Rails
3
+ module Extras
4
+ # A controller mixin to handle contact forms easily. If you don't use Page as your page class, you need to specify it.
5
+ # You also need to specify the contact form class you're using (which needs to inherit from Rooftop::Rails::Extras::ContactForm)
6
+ module ContactFormHandler
7
+ extend ActiveSupport::Concern
8
+
9
+ class_methods do
10
+ def contact_form=(klass)
11
+ @contact_form = klass
12
+ end
13
+
14
+ def contact_form
15
+ @contact_form
16
+ end
17
+
18
+ def page_class=(klass)
19
+ @page_class = klass
20
+ end
21
+
22
+ def page_class
23
+ @page_class || ::Page
24
+ end
25
+ end
26
+
27
+ def create
28
+ raise ArgumentError, "You need to call contact_form=(YourClass) in your controller, which must inherit from Rooftop::Rails::Extras::ContactForm" unless self.class.contact_form.ancestors.include?(Rooftop::Rails::Extras::ContactForm)
29
+ raise ArgumentError, "You need to include hidden fields for from_page and to_page IDs, so this controller can redirect appropriately" unless params.has_key?(:from_page) && params.has_key?(:to_page)
30
+ form = self.class.contact_form.new(contact_form_params)
31
+ from_page, to_page = *self.class.page_class.where(post__in: [params[:from_page], params[:to_page]], orderby: :post__in)
32
+ if form.deliver
33
+ redirect_to Rooftop::Rails::RouteResolver.new(:page, to_page.nested_path).resolve
34
+ else
35
+ redirect_to Rooftop::Rails::RouteResolver.new(:page, from_page.nested_path).resolve(contact_form_params.merge(errors: form.errors.keys)), notice: form.errors.full_messages.to_sentence
36
+ end
37
+ end
38
+
39
+ private
40
+ def contact_form_params
41
+ params.require(self.class.contact_form.to_s.underscore.to_sym).permit(self.class.contact_form.fields.keys)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,117 @@
1
+ module Rooftop
2
+ module Rails
3
+ module Extras
4
+ module NavigationHelper
5
+ def subnavigation_for(entity, opts = {})
6
+ default_opts = {
7
+ class: 'subnavigation-list',
8
+ current_class: 'is-current',
9
+ current: nil
10
+ }.merge(opts)
11
+ raise ArgumentError, "#{entity.class} isn't a nested class" unless entity.respond_to?(:resolved_children)
12
+ raise ArgumentError, "You passed a current #{entity.class.to_s.downcase} which isn't a #{entity.class} object" unless (default_opts[:current].nil? || default_opts[:current].is_a?(entity.class))
13
+ if entity.resolved_children.any?
14
+ # byebug
15
+ content_tag(:ul, class: default_opts[:class]) do
16
+ items = entity.resolved_children.collect do |child|
17
+ subnavigation_item_for(child, default_opts)
18
+ end
19
+ items.join.html_safe
20
+ end
21
+ end
22
+ end
23
+
24
+ def menu_for(menu, opts={})
25
+ default_opts = {
26
+ class: 'main-navigation-list',
27
+ current_class: 'is-current'
28
+ }.merge(opts)
29
+ content_tag(:ul, class: default_opts[:class]) do
30
+ items = menu.items.collect do |item|
31
+ menu_item_for(item, opts)
32
+ end
33
+ items.join.html_safe
34
+ end
35
+ end
36
+
37
+ def menu_item_for(item, opts={})
38
+ default_opts = {
39
+ current_class: 'is-current',
40
+ level: 1
41
+ }.merge(opts)
42
+ item_path = path_for_menu_item(item)
43
+ item_class = path_matches?(item_path) ? default_opts[:current_class] : ""
44
+ content_tag :li, class: item_class do
45
+ link = content_tag :a, href: item_path do
46
+ item.title.html_safe
47
+ end
48
+ child_links = ""
49
+ if item.children.present?
50
+ child_links = content_tag :ul, class: "menu-level-#{default_opts[:level]}" do
51
+ items = item.children.collect do |child|
52
+ menu_item_for(child, default_opts.merge({level: default_opts[:level] + 1}))
53
+ end
54
+ items.join.html_safe
55
+ end
56
+ end
57
+ link + child_links
58
+ end
59
+ end
60
+
61
+ def breadcrumbs_for(entity, opts = {})
62
+ default_opts = {
63
+ class: 'breadcrumbs',
64
+ current_class: 'is-current'
65
+ }.merge(opts)
66
+ content_tag :ul, class: default_opts[:class] do
67
+ items = entity.ancestors.reverse.inject({}) do |links,ancestor|
68
+ links[(links.present? ? "#{links.keys.last}/#{ancestor.slug}" : ancestor.slug)] = ancestor
69
+ links
70
+ end
71
+ items.merge!({"#{items.keys.last}/#{entity.slug}" => entity})
72
+ links = items.collect do |path, item|
73
+ item_class = (item.id == entity.id ? default_opts[:current_class] : "")
74
+ content_tag :li, class: item_class do
75
+ content_tag :a, href: "/#{path}" do
76
+ item.title
77
+ end
78
+ end
79
+ end
80
+ links.join.html_safe
81
+ end
82
+ end
83
+
84
+ private
85
+ def subnavigation_item_for(entity, opts = {})
86
+ list_opts = {}
87
+ opts.reverse_merge!({level: 1})
88
+ list_opts[:class] = opts[:current_class] if opts[:current].present? && opts[:current].id == entity.id
89
+ # The link to the entity
90
+ content_tag :li, list_opts do
91
+
92
+ link = content_tag :a, href: "/#{entity.nested_path}" do
93
+ entity.title.html_safe
94
+ end
95
+
96
+ child_links = ""
97
+
98
+ # Nested ul for children if necessary
99
+ if opts[:current].present? && (opts[:current].ancestors.collect(&:id).include?(entity.id) || opts[:current].id == entity.id)
100
+ children = entity.class.where(post_parent: entity.id)
101
+ if children.any?
102
+ child_links = content_tag :ul, class: "subnavigation-level-#{opts[:level]}" do
103
+ items = children.collect do |child|
104
+ subnavigation_item_for(child, opts.merge({level: opts[:level] + 1}))
105
+ end
106
+ items.join.html_safe
107
+ end
108
+ end
109
+ end
110
+ link + child_links
111
+ end
112
+ end
113
+
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,14 @@
1
+ module Rooftop
2
+ module Rails
3
+ module Extras
4
+ module RelatedFieldsHelper
5
+ def field_from_related_resource(resource, field)
6
+ field_data = resource[:advanced].select {|f| f[:fields].collect {|f| f[:name] == field.to_s}.any?}
7
+ if field_data.any?
8
+ field_data.first[:fields].find {|f| f[:name] == field.to_s}.try(:[],:value)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,51 @@
1
+ module Rooftop
2
+ module Rails
3
+ module Extras
4
+ class ContactForm < MailForm::Base
5
+ class << self
6
+ attr_accessor :fields, :subject, :to, :from, :headers
7
+ end
8
+
9
+ DEFAULT_FIELDS = {
10
+ name: :text_field,
11
+ email: :text_field,
12
+ message: :text_area
13
+ }
14
+
15
+ def self.inherited(base)
16
+ (base.fields || DEFAULT_FIELDS).keys.each do |field|
17
+ base.send(:attribute,field, {validate: true})
18
+ end
19
+
20
+ base.subject ||= "Contact form message"
21
+ base.to ||= "change.me@#{base.to_s.underscore}"
22
+ base.from ||= "change.me@#{base.to_s.underscore}"
23
+ base.headers ||= {}
24
+ end
25
+ #
26
+ # attribute :salutation
27
+ # attribute :first_name, validate: true
28
+ # attribute :last_name, validate: true
29
+ # attribute :email, validate: true
30
+ # attribute :phone
31
+ # attribute :message, validate: true
32
+ # attribute :nickname, captcha: true
33
+
34
+ # Declare the e-mail headers. It accepts anything the mail method
35
+ # in ActionMailer accepts.
36
+ def headers
37
+ {
38
+ subject: self.class.subject,
39
+ to: self.class.to,
40
+ from: %("Contact form" <#{self.class.from}>)
41
+ }.merge(self.class.headers)
42
+ end
43
+
44
+ def fields
45
+ self.class.fields
46
+ end
47
+
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,20 @@
1
+ module Rooftop
2
+ module Rails
3
+ module Extras
4
+ module PageRedirect
5
+ extend ActiveSupport::Concern
6
+
7
+ def redirect?
8
+ fields.respond_to?(:redirect_this_page) && fields.redirect_this_page == true && fields.respond_to?(:redirect_url) && fields.redirect_url.present?
9
+ end
10
+
11
+ def redirect_to
12
+ if redirect?
13
+ fields.redirect_url
14
+ end
15
+ end
16
+
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ module Rooftop
2
+ module Rails
3
+ module Extras
4
+ module ResolvedChildren
5
+ extend ActiveSupport::Concern
6
+
7
+ def resolved_children
8
+ child_ids = children.collect(&:id)
9
+
10
+ if child_ids.any?
11
+ self.class.where(post__in: child_ids)
12
+ else
13
+ []
14
+ end
15
+
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,48 @@
1
+ module Rooftop
2
+ module Rails
3
+ module Extras
4
+ # A module to search for free text in a resource. You can add extra fields to search the free text in, by specifying a proc to call (which should return the text you want to search)
5
+ module ResourceSearch
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ scope :search, ->(q) {
10
+ all.to_a.select do |item|
11
+ q = q.gsub(/[^a-zA-z1-9 ]/,"")
12
+ item.searchable_text.match(%r{#{q.downcase}})
13
+ end
14
+ }
15
+ end
16
+
17
+ class_methods do
18
+
19
+ # A method which takes a lambda which is expected to return a string of text for searching.
20
+ def add_search_field(field)
21
+ @search_fields ||= []
22
+ @search_fields << field
23
+ end
24
+ # A collection of lambdas, each of which returns a string
25
+ def search_fields
26
+ @search_fields ||= []
27
+ end
28
+ end
29
+
30
+ def searchable_text
31
+ remove_regex = /[^a-zA-z1-9 ]/
32
+ text = []
33
+ text << title.gsub(remove_regex,"").downcase
34
+ text << ActionController::Base.helpers.strip_tags(fields.content).gsub(remove_regex,"").downcase
35
+ # Iterate over the search field lambdas, and call each one, passing in self. Check the arity is 1, to ensure that the lambda is expecting the object
36
+ self.class.search_fields.each do |field|
37
+ if field.arity == 1
38
+ text << ActionController::Base.helpers.strip_tags(field.call(self)).gsub(remove_regex,"").downcase
39
+ end
40
+ end
41
+ text.join(" ")
42
+
43
+ end
44
+
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,21 @@
1
+ module Rooftop
2
+ class ExtrasGenerator < ::Rails::Generators::Base
3
+ desc "A wrapper around the common things we do with Rooftop and Rails. Creates a PagesController, Page model, sets up templates as we tend to "
4
+
5
+ def create_page_class
6
+ generate 'rooftop:page'
7
+ end
8
+
9
+ def install_pages_controller
10
+ generate 'rooftop:pages_controller'
11
+ end
12
+
13
+ def create_content_page_template
14
+ generate "rooftop:template content_page"
15
+ end
16
+
17
+ def create_post_class
18
+ generate 'rooftop:post post'
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,14 @@
1
+ module Rooftop
2
+ class PageGenerator < ::Rails::Generators::Base
3
+ source_root File.expand_path("../templates", __FILE__)
4
+ desc "Generate a page class, mixing in Rooftop::Page and Rooftop::Rails::Extras::ResourceSearch"
5
+ class_option :class_name, :type => :string, :desc => "The class name to generate", :default => "page"
6
+ def create_class
7
+ template 'page_class.rb.erb', 'app/models/page.rb'
8
+ end
9
+
10
+ def create_decorator
11
+ generate "decorator #{options[:class_name]}"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,27 @@
1
+ module Rooftop
2
+ class PagesControllerGenerator < ::Rails::Generators::Base
3
+ source_root File.expand_path("../templates", __FILE__)
4
+ desc "Generate a standardised PagesController and views. Assumes a PageDecorator and Page model"
5
+
6
+ def copy_template
7
+ template "pages_controller.rb.erb", "app/controllers/pages_controller.rb"
8
+ end
9
+
10
+ def create_views
11
+ template "application.html.erb", "app/views/layouts/application.html.erb"
12
+ template "show.html.erb", "app/views/pages/show.html.erb"
13
+ template "index.html.erb", "app/views/pages/index.html.erb"
14
+ end
15
+
16
+ def add_route
17
+ comment_lines 'config/routes.rb', /pages#.*/
18
+ route 'root to: "pages#index"'
19
+ route 'match "/*nested_path", via: [:get], to: "pages#show", as: :page'
20
+ inject_into_file 'config/routes.rb', before: 'match "/*nested_path"' do <<-'RUBY'
21
+ # IMPORTANT: this is a greedy catchall route - it needs to be the last route in the file.
22
+ RUBY
23
+ end
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,9 @@
1
+ module Rooftop
2
+ class PostGenerator < ::Rails::Generators::NamedBase
3
+ source_root File.expand_path("../templates", __FILE__)
4
+ desc "Generate a post class - requires the name of the class to create"
5
+ def create_class
6
+ template 'post_class.rb.erb', "app/models/#{name}.rb"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Rooftop
2
+ class TemplateGenerator < ::Rails::Generators::NamedBase
3
+ source_root File.expand_path("../templates", __FILE__)
4
+ desc "Generate a template in views/layouts/templates"
5
+ def copy_template
6
+ template "template.html.erb", "app/views/layouts/templates/#{name}.html.erb"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title><%= Rails.application.class.parent_name %></title>
5
+ <%%= stylesheet_link_tag 'application', media: 'all' %>
6
+ <%%= javascript_include_tag 'application' %>
7
+ <%%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%%= content_for?(:template) ? yield(:template) : yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,2 @@
1
+ <p>This renders the index method of the PagesController - generally the homepage. This view is available in <%%= __FILE__ %></p>
2
+
@@ -0,0 +1,4 @@
1
+ class <%= options[:class_name].classify %>
2
+ include Rooftop::Page
3
+ include Rooftop::Rails::Extras::ResourceSearch
4
+ end
@@ -0,0 +1,49 @@
1
+ class PagesController < ApplicationController
2
+ include Rooftop::Rails::NestedResource
3
+ nested_rooftop_resource :page
4
+ decorates_assigned :page, with: PageDecorator
5
+
6
+ prepend_before_action :redirect_page_if_required, only: :show
7
+ prepend_before_action :find_and_validate_page, only: :show
8
+
9
+ layout :determine_layout
10
+
11
+ # This is the homepage. We're assuming that the slug is 'home'
12
+ def index
13
+ @page = Page.find_by(slug: "home").first
14
+ get_homepage_content
15
+ end
16
+
17
+ # This is the method used to render every page on the site except the homepage
18
+ def show
19
+ end
20
+
21
+
22
+ private
23
+
24
+ # If there's a redirect required, do that instead of rendering.
25
+ def redirect_page_if_required
26
+ if @page.redirect?
27
+ redirect_to @page.fields.redirect_url and return
28
+ end
29
+ end
30
+
31
+ # Determine which layout to show, assuming a standardised approach of having a templates folder under layouts with all the templates in, named as underscored versions of the Rooftop template names.
32
+ def determine_layout
33
+ if action_name == "index"
34
+ "templates/homepage"
35
+ else
36
+ if @page.present? && @page.template.present? && template_exists?(@page.template.underscore,File.join("layouts/templates"))
37
+ "templates/#{@page.template.underscore}"
38
+ else
39
+ "templates/content_page"
40
+ end
41
+ end
42
+ end
43
+
44
+ def get_homepage_content
45
+ # This is where you put homepage-specific stuff - usually other calls to Rooftop which you assign to instance variables.
46
+ end
47
+
48
+
49
+ end
@@ -0,0 +1,3 @@
1
+ class <%= name.classify %>
2
+ include Rooftop::Post
3
+ end
@@ -0,0 +1,3 @@
1
+ <p>This renders the show() methods of PagesController, generally every page on the site except the homepage. It is available in <%%= __FILE__ %></p>
2
+ <p>The title of this page is <%%= @page.try(:title).try(:html_safe) %></p>
3
+
@@ -0,0 +1,8 @@
1
+ <%% content_for :template do %>
2
+ <p>This template is in <%%= __FILE__ %></p>
3
+
4
+ <%%= yield %>
5
+
6
+ <%% end %>
7
+
8
+ <%%= render template: "layouts/application" %>