thesis 0.0.4 → 0.1.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.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # Thesis
1
+ # Thesis ![Build Status](https://travis-ci.org/clearsightstudio/thesis.png)
2
2
 
3
- ### Thesis is a CMS gem that integrates as seamlessly as possible into your current Rails website.
3
+ ### Thesis is a Rails CMS gem that integrates as seamlessly as possible into your current Rails website.
4
4
 
5
5
  Most Rails content management systems make you conform to their system from the start,
6
6
  making it difficult to just "drop in" the gem and make it work with your CMS. Thesis
@@ -10,9 +10,12 @@ of the way.
10
10
  ### Requirements
11
11
 
12
12
  * Rails 3.2.x (or higher)
13
+ * ActiveRecord on MySQL, PostgresQL, or SQLite3
13
14
  * Ruby 1.9.3 (or higher)
15
+ * jQuery
16
+ * jQuery UI
14
17
 
15
- Thesis might work with earlier versions but that's not our focus.
18
+ Thesis *might* work with other versions but no guarantees.
16
19
 
17
20
  ## Getting Started
18
21
 
@@ -116,13 +119,11 @@ Referencing a content area in a page template will create one if it doesn't exis
116
119
 
117
120
  ### Routing
118
121
 
119
- Thesis will also add a route handler to your `routes.rb` file. This will
120
- automatically handle routes for pages you create with Thesis.
121
-
122
- thesis_routes
122
+ Thesis will automatically handle routes for pages you create with Thesis. Your
123
+ routes will take precedence over Thesis-created pages, so if you create a page
124
+ with Thesis called "About" and you already have a route for
125
+ `get "about" => "something#else"` Thesis won't show the page.
123
126
 
124
- **To improve performance, put it near the bottom of your `routes.rb` file.**
125
-
126
127
  ## Using the CMS
127
128
 
128
129
  ### Adding a Page
@@ -143,11 +144,12 @@ TODO
143
144
 
144
145
  ## What Thesis Isn't
145
146
 
146
- You can't have it all or it becomes the same as the other bloated content management systems
147
- out there. This is a list of what it's not and what it's not ever likely to be.
147
+ You can't have it all. Thesis isn't the same as other -bloated- full-functioned
148
+ content management systems out there. This is a list of what it's not now and
149
+ not likely to be in the future.
148
150
 
149
- We reserve the right to change our mind, however, especially with well planned and written
150
- pull requests to help prod us in the right direction. :-)
151
+ *We reserve the right to change our mind, however, especially with well planned and written
152
+ pull requests to help prod us in the right direction. :-)*
151
153
 
152
154
  1. A WordPress Replacement
153
155
  2. A full featured CMS
@@ -162,6 +164,13 @@ pull requests to help prod us in the right direction. :-)
162
164
  2. Create your feature branch (`git checkout -b my-new-feature`)
163
165
  3. Commit your changes (`git commit -am 'Add some feature'`)
164
166
  4. Write tests for your new feature
165
- 5. Run `bundle exec rspec` in the root directory to ensure that all tests pass.
167
+ 5. Run `rake spec` in the root directory to ensure that all tests pass.
166
168
  6. Push to the branch (`git push origin my-new-feature`)
167
169
  7. Create new Pull Request
170
+
171
+ ### Key Contributors
172
+
173
+ * Jamon Holmgren [@jamonholmgren](https://twitter.com/jamonholmgren)
174
+ * Daniel Berkompas [@dberkom](https://twitter.com/dberkom)
175
+ * The ClearSight Studio team
176
+
@@ -19,18 +19,14 @@ module Thesis
19
19
  end
20
20
 
21
21
  def copy_migrations
22
- migration_template "migrations/create_page.rb", "db/migrate/create_page.rb"
23
- migration_template "migrations/create_page_content.rb", "db/migrate/create_page_content.rb"
22
+ migration_template "migrations/thesis_create_page.rb", "db/migrate/thesis_create_page.rb"
23
+ migration_template "migrations/thesis_create_page_content.rb", "db/migrate/thesis_create_page_content.rb", { skip: true }
24
24
  end
25
25
 
26
26
  def create_folders
27
- copy_file "page_templates/default.html.slim", "app/views/page_templates/default.html.slim" if Slim
28
- copy_file "page_templates/default.html.haml", "app/views/page_templates/default.html.haml" if Haml
29
- copy_file "page_templates/default.html.erb", "app/views/page_templates/default.html.erb" unless Haml || Slim
30
- end
31
-
32
- def set_up_routes
33
- route "thesis_routes # This needs to be the last route!"
27
+ copy_file "page_templates/default.html.slim", "app/views/page_templates/default.html.slim" if defined? Slim
28
+ copy_file "page_templates/default.html.haml", "app/views/page_templates/default.html.haml" if defined? Haml
29
+ copy_file "page_templates/default.html.erb", "app/views/page_templates/default.html.erb" unless defined? Haml || defined? Slim
34
30
  end
35
31
 
36
32
  def install_js
@@ -48,15 +44,20 @@ module Thesis
48
44
  end
49
45
 
50
46
  def install_css
51
- filename = "app/assets/stylesheets/application.css.scss"
52
- existing = File.binread("#{filename}").include?("require thesis")
53
-
54
- if existing && generating?
55
- say_status("skipped", "insert into #{filename}", :yellow)
56
- else
57
- insert_into_file "#{filename}", after: %r{ *= require_self} do
58
- "\n *= require thesis"
47
+ filename = "app/assets/stylesheets/application.css"
48
+ filename = filename << ".scss" unless File.exists? filename
49
+ if File.exists? filename
50
+ existing = File.binread("#{filename}").include?("require thesis")
51
+
52
+ if existing && generating?
53
+ say_status("skipped", "insert into #{filename}", :yellow)
54
+ else
55
+ insert_into_file "#{filename}", after: %r{ *= require_self} do
56
+ "\n *= require thesis"
57
+ end
59
58
  end
59
+ else
60
+ say_status("skipped", "Couldn't insert into #{filename} -- doesn't exist")
60
61
  end
61
62
  end
62
63
 
@@ -1,5 +1,8 @@
1
- class CreatePage < ActiveRecord::Migration
1
+ class ThesisCreatePage < ActiveRecord::Migration
2
2
  def self.up
3
+ # Please note: if you change this (in the thesis gem) please
4
+ # update the /spec/spec_helper.rb to match.
5
+
3
6
  create_table :pages do |t|
4
7
  t.integer :parent_id
5
8
  t.string :name
@@ -1,5 +1,8 @@
1
- class CreatePageContent < ActiveRecord::Migration
1
+ class ThesisCreatePageContent < ActiveRecord::Migration
2
2
  def self.up
3
+ # Please note: if you change this (in the thesis gem) please
4
+ # update the /spec/spec_helper.rb to match.
5
+
3
6
  create_table :page_contents do |t|
4
7
  t.integer :page_id, null: false
5
8
  t.string :name, null: false
@@ -1,18 +1,5 @@
1
1
  module Thesis
2
2
  module ControllerHelpers
3
- module ClassMethods
4
- def class_method_here
5
- # Sample
6
- end
7
- end
8
-
9
- def self.included(base)
10
- raise ActiveRecordRequired.new("Currently, Thesis only works with ActiveRecord.") unless defined? ActiveRecord
11
-
12
- # base.extend ClassMethods
13
- # base.helper_method :class_method_here
14
- end
15
-
16
3
  def current_page
17
4
  @current_page ||= Page.where(slug: request.fullpath).first
18
5
  end
@@ -20,20 +7,9 @@ module Thesis
20
7
  def root_pages
21
8
  @root_pages ||= Page.where(parent_id: nil).order("sort_order ASC").all
22
9
  end
23
-
24
- def page_is_editable?(page)
25
- raise RequiredMethodNotImplemented.new("Add a `page_is_editable?(page)` method to your controller that returns true or false.")
26
- end
27
-
10
+
28
11
  def thesis_editor
29
12
  "<div id='thesis-editor'></div>".html_safe if page_is_editable?(current_page)
30
13
  end
31
14
  end
32
15
  end
33
-
34
- if defined? ActionController::Base
35
- ActionController::Base.class_eval do
36
- include Thesis::ControllerHelpers
37
- helper_method :current_page, :root_pages, :page_is_editable?, :thesis_editor
38
- end
39
- end
@@ -1,24 +1,39 @@
1
1
  module Thesis
2
- class ThesisController < ActionController::Base
3
- include ControllerHelpers
4
-
2
+ class ThesisController < ::ApplicationController
3
+ include Thesis::ControllerHelpers
4
+
5
5
  def show
6
6
  raise ActionController::RoutingError.new('Not Found') unless current_page
7
7
 
8
8
  if current_page.template && template_exists?("page_templates/#{current_page.template}")
9
- render "page_templates/#{current_page.template}"
9
+ render "page_templates/#{current_page.template}", layout: false
10
10
  else
11
11
  raise PageRequiresTemplate.new("Page requires a template but none was specified.")
12
12
  end
13
13
  end
14
14
 
15
- def new_page
15
+ def create_page
16
16
  page = Page.new
17
17
  return head :forbidden unless page_is_editable?(page)
18
18
 
19
19
  update_page_attributes page
20
20
 
21
- head page.save ? :ok : :not_acceptable
21
+ resp = {}
22
+
23
+ if page.save
24
+ resp[:page] = page
25
+ else
26
+ resp[:message] = page.errors.messages.first
27
+ end
28
+
29
+ render json: resp, status: page.valid? ? :ok : :not_acceptable
30
+ end
31
+
32
+ def delete_page
33
+ page = Page.where(slug: params[:slug].to_s).first
34
+ return head :forbidden unless page && page_is_editable?(page)
35
+
36
+ head page.destroy ? :ok : :not_acceptable
22
37
  end
23
38
 
24
39
  def update_page
@@ -38,16 +53,37 @@ module Thesis
38
53
  page_attributes.each { |a| page.send("#{a}=", params[a]) if params[a] }
39
54
  page
40
55
  end
41
-
42
- protected
43
-
44
- def page_is_editable?(page)
45
- appcon = ApplicationController.new
46
- if appcon.respond_to?("page_is_editable?")
47
- appcon.page_is_editable?(page)
56
+
57
+ def update_page_content
58
+ errors = false
59
+ error_message = "Unknown error."
60
+
61
+ page_contents = PageContent.where(id: params.keys).includes(:page).all
62
+ if page_contents.length.zero?
63
+ error_message = "That page doesn't exist anymore."
64
+ errors = true
48
65
  else
49
- raise RequiredMethodNotImplemented.new("Add a `page_is_editable?(page)` method to your controller that returns true or false.")
66
+ page_contents.each do |pc|
67
+ if page_is_editable? pc.page
68
+ pc.content = params[pc.id.to_s]
69
+ pc.save
70
+ else
71
+ errors = true
72
+ error_message = "You don't have permission to update this page."
73
+ end
74
+ end
50
75
  end
76
+
77
+ resp = {}
78
+ resp[:message] = error_message if errors
79
+
80
+ render json: resp, status: errors ? :not_acceptable : :ok
81
+ end
82
+
83
+ # The ApplicationController should implement this.
84
+ def page_is_editable?(page)
85
+ raise RequiredMethodNotImplemented.new("Add a `page_is_editable?(page)` method to your controller that returns true or false.") unless defined?(super)
86
+ super
51
87
  end
52
88
  end
53
89
  end
data/lib/thesis/engine.rb CHANGED
@@ -1,5 +1,14 @@
1
1
  module Thesis
2
2
  class Engine < ::Rails::Engine
3
- isolate_namespace Thesis
3
+ # isolate_namespace Thesis # We're accessing the application controller, so we can't do this.
4
+ initializer 'thesis.action_controller' do |app|
5
+ require "thesis/controllers/controller_helpers"
6
+ require "thesis/controllers/thesis_controller"
7
+
8
+ ActiveSupport.on_load(:action_controller) do
9
+ include ::Thesis::ControllerHelpers
10
+ helper_method :current_page, :root_pages, :page_is_editable?, :thesis_editor
11
+ end
12
+ end
4
13
  end
5
14
  end
@@ -4,12 +4,16 @@ module Thesis
4
4
 
5
5
  belongs_to :parent, class_name: "Page"
6
6
  has_many :subpages, class_name: "Page", foreign_key: "parent_id", order: "sort_order ASC"
7
- has_many :page_contents
7
+ has_many :page_contents, dependent: :destroy
8
8
 
9
9
  before_validation :update_slug
10
10
  after_save :update_subpage_slugs
11
11
 
12
- validates :slug, uniqueness: { message: "There's already a page like that. Change your page name." }, presence: true
12
+ validates :slug,
13
+ uniqueness: { message: "There's already a page like that. Change your page name." },
14
+ presence: true,
15
+ length: { minimum: 1 },
16
+ allow_blank: false
13
17
 
14
18
  def update_slug
15
19
  self.slug = "/" << self.name.parameterize
@@ -20,8 +24,8 @@ module Thesis
20
24
  subpages.each(&:save) if slug_changed?
21
25
  end
22
26
 
23
- def content(name, content_type = :html)
24
- pc = find_or_create_page_content(name, content_type)
27
+ def content(name, content_type = :html, opts = {})
28
+ pc = find_or_create_page_content(name, content_type, opts)
25
29
  pc.render
26
30
  end
27
31
 
@@ -31,9 +35,13 @@ module Thesis
31
35
 
32
36
  protected
33
37
 
34
- def find_or_create_page_content(name, content_type)
38
+ def find_or_create_page_content(name, content_type, opts = {})
35
39
  page_content = self.page_contents.where(name: name).first_or_create do |pc|
36
- pc.content = "Edit This Area"
40
+ pc.content = "<p>Edit This HTML Area</p>" if content_type == :html
41
+ pc.content = "Edit This Text Area" if content_type == :text
42
+ width = opts[:width] || 350
43
+ height = opts[:height] || 150
44
+ pc.content = "http://placehold.it/#{width}x#{height}" if content_type == :image
37
45
  end
38
46
  page_content.content_type = content_type
39
47
  page_content.save if page_content.changed?
@@ -9,7 +9,9 @@ module Thesis
9
9
  when :html
10
10
  render_html
11
11
  when :text
12
- render_text
12
+ render_plain_text
13
+ when :image
14
+ render_image
13
15
  else
14
16
  render_html
15
17
  end
@@ -19,22 +21,22 @@ module Thesis
19
21
 
20
22
  def render_html
21
23
  h = "<div class='thesis-content thesis-content-html' data-thesis-content-id='#{self.id}'>" +
22
- "#{self.content}" +
23
- "</div>"
24
+ "#{self.content}" +
25
+ "</div>"
24
26
  h.html_safe
25
27
  end
26
28
 
27
- def render_text
29
+ def render_plain_text
28
30
  h = "<span class='thesis-content thesis-content-text' data-thesis-content-id='#{self.id}'>" +
29
- "#{self.content}" +
30
- "</span>"
31
+ "#{self.content}" +
32
+ "</span>"
31
33
  h.html_safe
32
34
  end
33
35
 
34
36
  def render_image
35
37
  h = "<div class='thesis-content thesis-content-image'>" +
36
- "<img src='#{self.content}' />" +
37
- "</div>"
38
+ "<img src='#{self.content}' />" +
39
+ "</div>"
38
40
  h.html_safe
39
41
  end
40
42
  end
@@ -1,3 +1,3 @@
1
1
  module Thesis
2
- VERSION = "0.0.4"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/thesis.rb CHANGED
@@ -1,10 +1,8 @@
1
- require "thesis/engine"
2
1
  require "thesis/version"
3
2
  require "thesis/exceptions"
4
- require "thesis/controllers/controller_helpers" # Included into ActionController::Base
5
- require "thesis/controllers/thesis_controller"
3
+ require "thesis/controllers/controller_helpers"
6
4
  require "thesis/routing/route_constraint"
7
- require "thesis/routing/routes"
5
+ require "thesis/engine"
8
6
 
9
7
  # Models
10
8
  require "thesis/models/page"
@@ -1,68 +1,96 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Thesis::ThesisController do
4
- let(:page) { create :page, template: "default" }
5
-
6
- before do
7
- described_class.any_instance.stub(:render).and_return(page.title)
8
- described_class.any_instance.stub(:template_exists?).and_return(true)
9
- described_class.any_instance.stub(:page_is_editable?).and_return(true)
10
- end
4
+ let(:page) { create :page, template: "default" }
5
+ let(:page_content) { create :page_content, name: "Main", page: page }
6
+
7
+ before do
8
+ described_class.any_instance.stub(:render).and_return(page.title)
9
+ described_class.any_instance.stub(:template_exists?).and_return(true)
10
+ described_class.any_instance.stub(:page_is_editable?).and_return(true)
11
+ end
11
12
 
12
- describe "#show" do
13
- it "displays a page when it can be found" do
14
- make_request :show, path: page.slug
15
- @response.should_not be_nil
16
- end
13
+ describe "#show" do
14
+ it "displays a page when it can be found" do
15
+ make_request :show, path: page.slug
16
+ @response.should_not be_nil
17
+ end
17
18
 
18
- it "raises RoutingError when the page slug can't be found" do
19
- expect { make_request :show, path: "/nonexistent" }.to raise_error ActionController::RoutingError
20
- end
19
+ it "raises RoutingError when the page slug can't be found" do
20
+ expect { make_request :show, path: "/nonexistent" }.to raise_error ActionController::RoutingError
21
+ end
21
22
 
22
- it "raises PageRequiresTemplate when template can't be found" do
23
- described_class.any_instance.stub(:template_exists?).and_return(false)
24
- expect { make_request :show, path: page.slug }.to raise_error Thesis::PageRequiresTemplate
25
- end
26
- end
23
+ it "raises PageRequiresTemplate when template can't be found" do
24
+ described_class.any_instance.stub(:template_exists?).and_return(false)
25
+ expect { make_request :show, path: page.slug }.to raise_error Thesis::PageRequiresTemplate
26
+ end
27
+ end
27
28
 
28
- describe "#new_page" do
29
- context "when the page can be edited" do
30
- it "creates a page" do
31
- make_request :new_page, name: "New Page"
32
- page = Thesis::Page.last
33
- expect(page.name).to eq "New Page"
34
- end
35
- end
29
+ describe "#create_page" do
30
+ context "when the page can be edited" do
31
+ it "creates a page" do
32
+ make_request :create_page, name: "New Page"
33
+ page = Thesis::Page.last
34
+ expect(page.name).to eq "New Page"
35
+ end
36
+ end
36
37
 
37
- context "when the page can't be edited" do
38
- before { described_class.any_instance.stub(:page_is_editable?).and_return(false) }
38
+ context "when the page can't be edited" do
39
+ before { described_class.any_instance.stub(:page_is_editable?).and_return(false) }
39
40
 
40
- it "returns a status of 403 'Forbidden'" do
41
- make_request :new_page, name: "New Page"
42
- expect(@response.status).to eq 403
43
- end
44
- end
45
- end
41
+ it "returns a status of 403 'Forbidden'" do
42
+ make_request :create_page, name: "New Page"
43
+ expect(@response.status).to eq 403
44
+ end
45
+ end
46
+ end
46
47
 
47
- describe "#update_page" do
48
- context "when the page can be edited" do
49
- it "updates the attributes of the page" do
50
- make_request :update_page, path: page.slug, name: "New Name"
51
- page.reload
52
- expect(page.name).to eq "New Name"
53
- expect(page.slug).to eq "/new-name"
54
- end
55
- end
56
- end
48
+ describe "#update_page" do
49
+ context "when the page can be edited" do
50
+ it "updates the attributes of the page" do
51
+ make_request :update_page, path: page.slug, name: "New Name"
52
+ page.reload
53
+ expect(page.name).to eq "New Name"
54
+ expect(page.slug).to eq "/new-name"
55
+ end
56
+ end
57
+ end
58
+
59
+ describe "#update_page_content" do
60
+ context "when the page can be edited" do
61
+ it "updates the content on the page" do
62
+ page_content.content = "I'm the content"
63
+ page_content.save
64
+
65
+ make_request :update_page_content, { method: :put, page_content.id => "New content" }
66
+
67
+ expect(page_content.reload.content).to eq "New content"
68
+ end
69
+ end
70
+
71
+ context "when the page can't be edited" do
72
+ before { described_class.any_instance.stub(:page_is_editable?).and_return(false) }
73
+
74
+ it "doesn't update the page_content" do
75
+ page_content.content = "I'm the content"
76
+ page_content.save
77
+
78
+ make_request :update_page_content, { method: :post, page_content.id => "New content" }
79
+
80
+ expect(page_content.reload.content).to eq "I'm the content"
81
+ end
82
+ end
83
+ end
57
84
  end
58
85
 
59
86
  # Mock a request to the controller without
60
87
  # Using Rails routing. Makes the response available
61
88
  # in the @response instance variable.
62
89
  def make_request(action, params = {})
63
- path = params[:path] || "/"
64
- env = Rack::MockRequest.env_for(path, :params => params.except(:path), method: :post)
65
- status, headers, body = described_class.action(action).call(env)
66
- @response = ActionDispatch::TestResponse.new(status, headers, body)
67
- @controller = body.request.env['action_controller.instance']
90
+ path = params[:path] || "/"
91
+ method = params[:method] || "post"
92
+ env = Rack::MockRequest.env_for(path, :params => params.except(:path).except(:method), method: method)
93
+ status, headers, body = described_class.action(action).call(env)
94
+ @response = ActionDispatch::TestResponse.new(status, headers, body)
95
+ @controller = body.request.env['action_controller.instance']
68
96
  end
@@ -1,7 +1,7 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Thesis::PageContent do
4
- let(:page) { create(:page) }
4
+ let(:page) { create(:page) }
5
5
  let(:page_content) { create(:page_content, attributes.merge(page_id: page.id)) }
6
6
 
7
7
  describe "#render" do
@@ -14,20 +14,19 @@ describe Thesis::PageContent do
14
14
  it { should match page_content.content }
15
15
  end
16
16
 
17
- context "when the content type is 'text'" do
18
- let(:attributes) {{ content_type: "text" }}
17
+ context "when the content type is 'text'" do
18
+ let(:attributes) {{ content_type: "text" }}
19
19
 
20
- it { should match /thesis-content-text/ }
21
- it { should match page_content.content }
22
- end
20
+ it { should match /thesis-content-text/ }
21
+ it { should match page_content.content }
22
+ end
23
23
 
24
- context "when the content type is 'image'" do
25
- let(:attributes) {{ content_type: "image" }}
26
-
27
- pending "currently renders as 'html'. Consider using #render_image in #render."
28
- # it { should match /thesis-content-image/ }
29
- # it { should match /img/ }
30
- # it { should match page_content.content }
31
- end
32
- end
24
+ context "when the content type is 'image'" do
25
+ let(:attributes) {{ content_type: "image" }}
26
+
27
+ it { should match /thesis-content-image/ }
28
+ it { should match /img/ }
29
+ it { should match page_content.content }
30
+ end
31
+ end
33
32
  end
data/spec/spec_helper.rb CHANGED
@@ -1,12 +1,24 @@
1
1
  require 'rubygems'
2
2
  require 'bundler/setup'
3
- require 'rails/all'
4
3
  require 'factory_girl'
5
4
  require 'rspec/autorun'
6
5
  require 'database_cleaner'
6
+ require 'rails/all'
7
+
8
+ # Add a fake ApplicationController for testing.
9
+ class ApplicationController < ActionController::Base
10
+ def page_is_editable?(page)
11
+ true
12
+ end
13
+ end
14
+
15
+ # Require thesis
16
+ require "thesis"
7
17
 
8
- # Require all the Thesis files
9
- Dir[File.join('.', '/lib/thesis/**/*.rb')].each {|file| require file }
18
+ # Manually run the Rails initializer. Thanks to @JoshReedSchramm for the code.
19
+ initializer = Thesis::Engine.initializers.select { |i| i.name == "thesis.action_controller" }.first
20
+ initializer.run
21
+ # Dir[File.join('.', '/lib/thesis/**/*.rb')].each {|file| require file }
10
22
 
11
23
  # Load Factories
12
24
  FactoryGirl.find_definitions