nocms-pages 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +674 -0
  3. data/README.md +4 -0
  4. data/Rakefile +21 -0
  5. data/app/assets/stylesheets/pages/application.css +13 -0
  6. data/app/controllers/no_cms/pages/application_controller.rb +6 -0
  7. data/app/controllers/no_cms/pages/pages_controller.rb +12 -0
  8. data/app/helpers/no_cms/pages/application_helper.rb +6 -0
  9. data/app/models/no_cms/pages/block.rb +146 -0
  10. data/app/models/no_cms/pages/concerns/translation_scopes.rb +16 -0
  11. data/app/models/no_cms/pages/page.rb +37 -0
  12. data/app/views/layouts/nocms/pages/application.html.erb +14 -0
  13. data/app/views/no_cms/pages/blocks/_default.html.erb +8 -0
  14. data/app/views/no_cms/pages/pages/show.html.erb +6 -0
  15. data/config/locales/en.yml +18 -0
  16. data/config/routes.rb +6 -0
  17. data/db/migrate/20140226123742_create_no_cms_pages.rb +14 -0
  18. data/db/migrate/20140227100626_create_no_cms_blocks.rb +16 -0
  19. data/db/migrate/20140227143723_add_fields_info_to_no_cms_block.rb +5 -0
  20. data/db/migrate/20140228112643_add_slug_to_no_cms_page.rb +5 -0
  21. data/db/migrate/20140303092727_add_awesome_nested_set_to_no_cms_pages.rb +8 -0
  22. data/db/migrate/20140303100908_add_path_to_no_cms_page.rb +5 -0
  23. data/db/migrate/20140303123845_add_poisition_to_no_cms_block.rb +5 -0
  24. data/db/migrate/20140303145615_add_template_to_no_cms_page.rb +5 -0
  25. data/db/migrate/20140313171000_add_draft_to_block.rb +5 -0
  26. data/db/migrate/20140314110439_add_draft_to_pages.rb +5 -0
  27. data/lib/generators/nocms/pages_generator.rb +15 -0
  28. data/lib/generators/nocms/templates/config/initializers/nocms/pages.rb +25 -0
  29. data/lib/nocms/pages/configuration.rb +18 -0
  30. data/lib/nocms/pages/engine.rb +10 -0
  31. data/lib/nocms/pages/routes/page_found_constraint.rb +9 -0
  32. data/lib/nocms/pages/routes.rb +1 -0
  33. data/lib/nocms/pages/version.rb +5 -0
  34. data/lib/nocms_pages.rb +8 -0
  35. data/lib/tasks/pages_tasks.rake +4 -0
  36. metadata +153 -0
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ nocms-pages
2
+ ===========
3
+
4
+ Gem with content page functionality independent from any CMS embeddable in any Rails 4 app
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Pages'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+ Bundler::GemHelper.install_tasks
21
+
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,6 @@
1
+ module NoCms
2
+ module Pages
3
+ class ApplicationController < ActionController::Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,12 @@
1
+ require_dependency "no_cms/pages/application_controller"
2
+
3
+ module NoCms::Pages
4
+ class PagesController < ApplicationController
5
+ def show
6
+ @page = Page.no_drafts.where(path: "/#{params[:path]}").first
7
+ raise ActionController::RoutingError.new('Not Found') if @page.nil?
8
+
9
+ render @page.template unless @page.template.blank?
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,6 @@
1
+ module NoCms
2
+ module Pages
3
+ module ApplicationHelper
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,146 @@
1
+ module NoCms::Pages
2
+ class Block < ActiveRecord::Base
3
+
4
+ include Concerns::TranslationScopes
5
+
6
+ scope :drafts, ->() { where_with_locale(draft: true) }
7
+ scope :no_drafts, ->() { where_with_locale(draft: false) }
8
+
9
+ belongs_to :page
10
+
11
+ attr_reader :cached_objects
12
+
13
+ translates :layout, :fields_info, :draft
14
+
15
+ class Translation
16
+ serialize :fields_info, Hash
17
+ end
18
+
19
+ after_initialize :set_blank_fields
20
+ after_create :set_default_position
21
+ before_save :save_related_objects
22
+
23
+ validates :fields_info, presence: { allow_blank: true }
24
+ validates :page, :layout, presence: true
25
+
26
+ def layout_config
27
+ NoCms::Pages.block_layouts.stringify_keys[layout]
28
+ end
29
+
30
+ def template
31
+ layout_config[:template] if layout_config
32
+ end
33
+
34
+ def has_field? field
35
+ # We have the field if...
36
+ !layout_config.nil? && # We have a layout configuration AND
37
+ (
38
+ !layout_config[:fields].symbolize_keys[field.to_sym].nil? || # We have this field OR
39
+ !layout_config[:fields].symbolize_keys[field.to_s.gsub(/\_id$/, '').to_sym].nil? # we remove the final _id and then we have the field
40
+ )
41
+ end
42
+
43
+ def field_type field
44
+ return nil unless has_field?(field)
45
+ layout_config[:fields].symbolize_keys[field.to_sym]
46
+ end
47
+
48
+ def read_field field
49
+ return nil unless has_field?(field)
50
+
51
+ value = fields_info[field.to_sym] || # first, we get the value
52
+ @cached_objects[field.to_sym] # or we get it from the cached objects
53
+
54
+ # If value is still nil, but the field exists we must get the object from the database
55
+ if value.nil?
56
+ field_id = fields_info["#{field}_id".to_sym]
57
+ value = @cached_objects[field.to_sym] = field_type(field).find(field_id) unless field_id.nil?
58
+ end
59
+
60
+ value
61
+ end
62
+
63
+ def write_field field, value
64
+ return nil unless has_field?(field)
65
+ field_type = field_type field
66
+ # If field type is a model then we update the cached object
67
+ if field_type.is_a?(Class) && field_type < ActiveRecord::Base
68
+ # First, we initialize the object if we don't read the object (it loads it into the cached objects)
69
+ @cached_objects[field.to_sym] = field_type.new if read_field(field).nil?
70
+ # Then, assign attributes
71
+ @cached_objects[field.to_sym].assign_attributes value
72
+ else # If it's a model then a new object or update the previous one
73
+ self.fields_info = fields_info.merge field.to_sym => value # when updating through an object (i.e. the page updates through nested attributes) fields_info[field.to_sym] = value doesn't work. Kudos to Rubo for this fix
74
+ end
75
+ end
76
+
77
+ # In this missing method we check wether we're asking for one field
78
+ # in which case we will read or write ir
79
+ def method_missing(m, *args, &block)
80
+ # We get the name of the field stripping out the '=' for writers
81
+ field = m.to_s
82
+ write_accessor = field.ends_with? '='
83
+ field.gsub!(/\=$/, '')
84
+
85
+ # If this field actually exists, then we write it or read it.
86
+ if has_field?(field)
87
+ write_accessor ?
88
+ write_field(field, args.first) :
89
+ read_field(field.to_sym)
90
+ else
91
+ super
92
+ end
93
+ end
94
+
95
+ # When we are assigning attributes (this method is called in new, create...)
96
+ # we must split those fields from our current layout and those who are not
97
+ # (they must be attributes).
98
+ # Attributes are processed the usual way and fields are written later
99
+ def assign_attributes new_attributes
100
+ fields = []
101
+
102
+ set_blank_fields
103
+
104
+ # We get the layout
105
+ new_layout = new_attributes[:layout] || new_attributes['layout']
106
+ self.layout = new_layout unless new_layout.nil?
107
+
108
+ # And now separate fields and attributes
109
+ fields = new_attributes.select{|k, _| has_field? k }.symbolize_keys
110
+ new_attributes.reject!{|k, _| has_field? k }
111
+
112
+ super(new_attributes)
113
+
114
+ Rails.logger.info "Writing #{fields.inspect} to #{self.layout} block"
115
+
116
+ fields.each do |field_name, value|
117
+ self.write_field field_name, value
118
+ end
119
+ end
120
+
121
+ def reload
122
+ @cached_objects = {}
123
+ super
124
+ end
125
+
126
+ private
127
+
128
+ def set_blank_fields
129
+ self.fields_info ||= {}
130
+ @cached_objects ||= {}
131
+ end
132
+
133
+ def set_default_position
134
+ self.update_attribute :position, ((page.blocks.pluck(:position).compact.max || 0) + 1) if self.position.blank?
135
+ end
136
+
137
+ def save_related_objects
138
+ cached_objects.each do |field, object|
139
+ if object.is_a?(ActiveRecord::Base) && object.changed?
140
+ object.save!
141
+ fields_info["#{field}_id".to_sym] = object.id
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,16 @@
1
+ module NoCms
2
+ module Pages
3
+ module Concerns
4
+ module TranslationScopes
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ scope :where_with_locale, ->(where_params, locale = ::I18n.locale) {
9
+ with_translations(locale).where(self::Translation.table_name => where_params)
10
+ }
11
+ end
12
+
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,37 @@
1
+ module NoCms::Pages
2
+ class Page < ActiveRecord::Base
3
+
4
+ include Concerns::TranslationScopes
5
+
6
+ scope :drafts, ->() { where_with_locale(draft: true) }
7
+ scope :no_drafts, ->() { where_with_locale(draft: false) }
8
+
9
+ acts_as_nested_set
10
+
11
+ has_many :blocks, inverse_of: :page, class_name: 'NoCms::Pages::Block'
12
+ accepts_nested_attributes_for :blocks, allow_destroy: true
13
+
14
+ translates :title, :body, :slug, :path, :draft
15
+
16
+ validates :title, :body, presence: true
17
+ validates :slug, presence: { allow_blank: true }
18
+
19
+ before_validation :set_slug_and_path
20
+ after_move :rebuild_path
21
+
22
+ def set_slug_and_path
23
+ self.slug = title.parameterize if slug.nil? && !title.nil?
24
+ self.rebuild_path if path.nil?
25
+ end
26
+
27
+ def rebuild_path
28
+ self.update_attribute :path, "#{ancestors.map(&:path).join}/#{slug}"
29
+ end
30
+
31
+ def self.templates
32
+ Dir[Rails.root.join('app', 'views', 'no_cms', 'pages', 'pages', '*.html.erb').to_s]. # We get all page templates
33
+ map { |f| File.basename(f, '.html.erb') }.uniq.sort # And get their names
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Pages</title>
5
+ <%= stylesheet_link_tag "pages/application", media: "all" %>
6
+ <%= javascript_include_tag "pages/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,8 @@
1
+ <div class='block'>
2
+ <% block.fields_info.each do |field_name, field_content| %>
3
+ <div class="<%=field_name %>">
4
+ <h2><%=field_name %></h2>
5
+ <p><%= field_content %></p>
6
+ </div>
7
+ <% end %>
8
+ </div>
@@ -0,0 +1,6 @@
1
+ <h1><%= @page.title %></h1>
2
+ <p><%= @page.body %></p>
3
+
4
+ <% @page.blocks.no_drafts.each do |block| %>
5
+ <%= render "no_cms/pages/blocks/#{block.template}", block: block %>
6
+ <% end %>
@@ -0,0 +1,18 @@
1
+ en:
2
+ activerecord:
3
+ models:
4
+ no_cms/pages/page: Page
5
+ no_cms/pages/block: Content Block
6
+ attributes:
7
+ no_cms/pages/page:
8
+ title: Title
9
+ body: Body
10
+ no_cms/pages/block:
11
+ layout: Layout
12
+ title: Title
13
+ body: Body
14
+ no_cms:
15
+ pages:
16
+ blocks:
17
+ layouts:
18
+ default: Default layout
data/config/routes.rb ADDED
@@ -0,0 +1,6 @@
1
+ NoCms::Pages::Engine.routes.draw do
2
+
3
+ get '*path', to: 'pages#show', constraints: NoCms::Pages::Routes::PageFoundConstraint
4
+ root to: 'pages#show', path:''
5
+
6
+ end
@@ -0,0 +1,14 @@
1
+ class CreateNoCmsPages < ActiveRecord::Migration
2
+ def change
3
+ create_table :no_cms_pages_pages do |t|
4
+ t.timestamps
5
+ end
6
+
7
+ create_table :no_cms_pages_page_translations do |t|
8
+ t.belongs_to :no_cms_pages_page, index: true
9
+ t.string :locale
10
+ t.string :title
11
+ t.string :body
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ class CreateNoCmsBlocks < ActiveRecord::Migration
2
+ def change
3
+ create_table :no_cms_pages_blocks do |t|
4
+ t.belongs_to :page, index: true
5
+
6
+ t.timestamps
7
+ end
8
+
9
+ create_table :no_cms_pages_block_translations do |t|
10
+ t.belongs_to :no_cms_pages_block, index: true
11
+ t.string :locale
12
+ t.string :layout
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,5 @@
1
+ class AddFieldsInfoToNoCmsBlock < ActiveRecord::Migration
2
+ def change
3
+ add_column :no_cms_pages_block_translations, :fields_info, :longtext, default: Hash.new.to_yaml
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddSlugToNoCmsPage < ActiveRecord::Migration
2
+ def change
3
+ add_column :no_cms_pages_page_translations, :slug, :string
4
+ end
5
+ end
@@ -0,0 +1,8 @@
1
+ class AddAwesomeNestedSetToNoCmsPages < ActiveRecord::Migration
2
+ def change
3
+ add_column :no_cms_pages_pages, :parent_id, :integer
4
+ add_column :no_cms_pages_pages, :lft, :integer
5
+ add_column :no_cms_pages_pages, :rgt, :integer
6
+ add_column :no_cms_pages_pages, :depth, :integer
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ class AddPathToNoCmsPage < ActiveRecord::Migration
2
+ def change
3
+ add_column :no_cms_pages_page_translations, :path, :string
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddPoisitionToNoCmsBlock < ActiveRecord::Migration
2
+ def change
3
+ add_column :no_cms_pages_blocks, :position, :integer
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddTemplateToNoCmsPage < ActiveRecord::Migration
2
+ def change
3
+ add_column :no_cms_pages_pages, :template, :string
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddDraftToBlock < ActiveRecord::Migration
2
+ def change
3
+ add_column :no_cms_pages_block_translations, :draft, :boolean, default: false
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddDraftToPages < ActiveRecord::Migration
2
+ def change
3
+ add_column :no_cms_pages_page_translations, :draft, :boolean, default: false
4
+ end
5
+ end
@@ -0,0 +1,15 @@
1
+ module NoCms
2
+ class PagesGenerator < Rails::Generators::Base
3
+
4
+ source_root File.expand_path("../templates/", __FILE__)
5
+
6
+ def generate_nocms_pages_initializer
7
+ template "config/initializers/nocms/pages.rb", File.join(destination_root, "config", "initializers", "nocms", "pages.rb")
8
+ end
9
+
10
+ def self.namespace
11
+ "nocms:pages"
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ NoCms::Pages.configure do |config|
2
+
3
+ # In this section we configure block layouts. It's just an array of layouts, each consisting on a hash.
4
+ # Each layout has a template and a list of fields with a type.
5
+ # E.g: config.block_layouts = {
6
+ # 'title-long_text' => {
7
+ # template: 'title-long_text',
8
+ # fields: {
9
+ # title: :string,
10
+ # long_text: :text
11
+ # }
12
+ # },
13
+ # 'title-3_columns_text' => {
14
+ # template: 'title-3_columns_text',
15
+ # fields: {
16
+ # title: :string,
17
+ # column_1: :text,
18
+ # column_2: :text,
19
+ # column_3: :text
20
+ # }
21
+ # }
22
+ # }
23
+ # config.block_layouts = {}
24
+
25
+ end
@@ -0,0 +1,18 @@
1
+ module NoCms
2
+ module Pages
3
+ include ActiveSupport::Configurable
4
+
5
+ config_accessor :block_layouts
6
+
7
+ self.block_layouts = {
8
+ 'default' => {
9
+ template: 'default',
10
+ fields: {
11
+ title: :string,
12
+ body: :text
13
+ }
14
+ }
15
+ }
16
+
17
+ end
18
+ end
@@ -0,0 +1,10 @@
1
+ require 'globalize'
2
+ require 'awesome_nested_set'
3
+
4
+ module NoCms
5
+ module Pages
6
+ class Engine < ::Rails::Engine
7
+ isolate_namespace NoCms::Pages
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ module NoCms::Pages::Routes
2
+ class PageFoundConstraint
3
+
4
+ # A filter URL must NOT have a qualification as the first of its path filters
5
+ def self.matches?(request)
6
+ @page = NoCms::Pages::Page.no_drafts.where(path: "/#{request.params[:path]}").exists?
7
+ end
8
+ end
9
+ end
@@ -0,0 +1 @@
1
+ require 'nocms/pages/routes/page_found_constraint'
@@ -0,0 +1,5 @@
1
+ module NoCms
2
+ module Pages
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,8 @@
1
+ require "nocms/pages/engine"
2
+ require 'nocms/pages/configuration'
3
+ require "nocms/pages/routes"
4
+
5
+ module NoCms
6
+ module Pages
7
+ end
8
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :pages do
3
+ # # Task goes here
4
+ # end