enki-engine 0.0.2
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/.gitignore +13 -0
- data/.rspec +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE +284 -0
- data/README.textile +112 -0
- data/Rakefile +13 -0
- data/TODO.textile +8 -0
- data/app/assets/images/admin/flash_bg.gif +0 -0
- data/app/assets/images/admin/future_bg.png +0 -0
- data/app/assets/images/admin/gray_bg.gif +0 -0
- data/app/assets/images/admin/new_button.png +0 -0
- data/app/assets/images/admin/silver_bg.gif +0 -0
- data/app/assets/images/admin/submit_bg.gif +0 -0
- data/app/assets/images/admin/subnav_bg.gif +0 -0
- data/app/assets/images/openid_icon.png +0 -0
- data/app/assets/images/rails.png +0 -0
- data/app/assets/images/silk/arrow_undo.png +0 -0
- data/app/assets/images/silk/delete.png +0 -0
- data/app/assets/images/silk/pencil.png +0 -0
- data/app/assets/javascripts/admin/actions.js +18 -0
- data/app/assets/javascripts/admin/comments.js +3 -0
- data/app/assets/javascripts/admin/common.js +109 -0
- data/app/assets/javascripts/admin/dashboard.js +33 -0
- data/app/assets/javascripts/admin/edit-preview.js +40 -0
- data/app/assets/javascripts/admin/pages.js +1 -0
- data/app/assets/javascripts/admin/posts.js +1 -0
- data/app/assets/javascripts/admin/shortcut.js +223 -0
- data/app/assets/javascripts/admin.js +17 -0
- data/app/assets/javascripts/application.js +15 -0
- data/app/assets/javascripts/common.js +22 -0
- data/app/assets/javascripts/enki.js +13 -0
- data/app/assets/javascripts/live-comment-preview.js +36 -0
- data/app/assets/stylesheets/admin.css +304 -0
- data/app/assets/stylesheets/application.css.scss +486 -0
- data/app/assets/stylesheets/humanmsg.css +112 -0
- data/app/assets/stylesheets/login.css +65 -0
- data/app/controllers/enki/admin/base_controller.rb +15 -0
- data/app/controllers/enki/admin/comments_controller.rb +52 -0
- data/app/controllers/enki/admin/dashboard_controller.rb +12 -0
- data/app/controllers/enki/admin/health_controller.rb +20 -0
- data/app/controllers/enki/admin/pages_controller.rb +97 -0
- data/app/controllers/enki/admin/posts_controller.rb +104 -0
- data/app/controllers/enki/admin/undo_items_controller.rb +43 -0
- data/app/controllers/enki/application_controller.rb +21 -0
- data/app/controllers/enki/archives_controller.rb +7 -0
- data/app/controllers/enki/base_controller.rb +5 -0
- data/app/controllers/enki/comments_controller.rb +43 -0
- data/app/controllers/enki/pages_controller.rb +7 -0
- data/app/controllers/enki/posts_controller.rb +27 -0
- data/app/helpers/enki/admin/navigation_helper.rb +10 -0
- data/app/helpers/enki/application_helper.rb +35 -0
- data/app/helpers/enki/date_helper.rb +15 -0
- data/app/helpers/enki/form_helper.rb +7 -0
- data/app/helpers/enki/host_helper.rb +19 -0
- data/app/helpers/enki/navigation_helper.rb +23 -0
- data/app/helpers/enki/page_title_helper.rb +33 -0
- data/app/helpers/enki/posts_helper.rb +9 -0
- data/app/helpers/enki/tag_helper.rb +7 -0
- data/app/helpers/enki/url_helper.rb +25 -0
- data/app/models/enki/base/post.rb +153 -0
- data/app/models/enki/comment.rb +75 -0
- data/app/models/enki/comment_activity.rb +33 -0
- data/app/models/enki/delete_comment_undo.rb +33 -0
- data/app/models/enki/delete_page_undo.rb +32 -0
- data/app/models/enki/delete_post_undo.rb +36 -0
- data/app/models/enki/page.rb +42 -0
- data/app/models/enki/post.rb +4 -0
- data/app/models/enki/stats.rb +19 -0
- data/app/models/enki/tag.rb +19 -0
- data/app/models/enki/tagging.rb +7 -0
- data/app/models/enki/undo_item.rb +10 -0
- data/app/views/enki/admin/comments/_comment.html.erb +12 -0
- data/app/views/enki/admin/comments/index.html.erb +30 -0
- data/app/views/enki/admin/comments/show.html.erb +9 -0
- data/app/views/enki/admin/dashboard/show.html.erb +61 -0
- data/app/views/enki/admin/health/index.html.erb +3 -0
- data/app/views/enki/admin/pages/_form.html.erb +3 -0
- data/app/views/enki/admin/pages/_page.html.erb +12 -0
- data/app/views/enki/admin/pages/index.html.erb +25 -0
- data/app/views/enki/admin/pages/new.html.erb +8 -0
- data/app/views/enki/admin/pages/show.html.erb +8 -0
- data/app/views/enki/admin/posts/_form.html.erb +11 -0
- data/app/views/enki/admin/posts/_post.html.erb +12 -0
- data/app/views/enki/admin/posts/_taggings_form.html.erb +4 -0
- data/app/views/enki/admin/posts/_upload_form.html.erb +0 -0
- data/app/views/enki/admin/posts/index.html.erb +25 -0
- data/app/views/enki/admin/posts/new.html.erb +8 -0
- data/app/views/enki/admin/posts/show.html.erb +8 -0
- data/app/views/enki/admin/undo_items/index.html.erb +24 -0
- data/app/views/enki/archives/index.html.erb +17 -0
- data/app/views/enki/comments/_comment.html.erb +4 -0
- data/app/views/enki/pages/_page.html.erb +3 -0
- data/app/views/enki/pages/show.html.erb +5 -0
- data/app/views/enki/posts/_post.html.erb +13 -0
- data/app/views/enki/posts/index.atom.builder +27 -0
- data/app/views/enki/posts/index.html.erb +15 -0
- data/app/views/enki/posts/show.html.erb +37 -0
- data/autotest/discover.rb +2 -0
- data/config/cucumber.yml +8 -0
- data/config/enki.yml.sample +16 -0
- data/config/initializers/enki_ext.rb +3 -0
- data/config/initializers/set_chronic_timezone.rb +1 -0
- data/config/initializers/verification.rb +135 -0
- data/config/routes.rb +34 -0
- data/db/migrate/20110709024316_initialize_db.rb +97 -0
- data/db/seeds.rb +8 -0
- data/enki-engine.gemspec +47 -0
- data/features/admin_dashboard.feature +10 -0
- data/features/admin_health.feature +10 -0
- data/features/admin_undo.feature +20 -0
- data/features/browsing.feature +16 -0
- data/features/step_definitions/admin.rb +27 -0
- data/features/step_definitions/browsing.rb +3 -0
- data/features/step_definitions/posts.rb +11 -0
- data/features/step_definitions/web_steps.rb +1 -0
- data/features/support/env.rb +59 -0
- data/features/support/paths.rb +35 -0
- data/features/support/selectors.rb +39 -0
- data/lib/core_extensions/object.rb +9 -0
- data/lib/core_extensions/string.rb +22 -0
- data/lib/enki/config.rb +44 -0
- data/lib/enki/engine.rb +19 -0
- data/lib/enki/html5_tags.rb +8 -0
- data/lib/enki/pagination_shim.rb +25 -0
- data/lib/enki/version.rb +3 -0
- data/lib/enki.rb +14 -0
- data/lib/enki_formatter.rb +11 -0
- data/lib/tag_list.rb +2 -0
- data/lib/tags_helper.rb +13 -0
- data/lib/tasks/cucumber.rake +65 -0
- data/lib/tasks/enki.rake +29 -0
- data/lib/undo_failed.rb +2 -0
- data/script/cucumber +10 -0
- data/spec/controllers/admin/comments_controller_spec.rb +140 -0
- data/spec/controllers/admin/dashboard_controller_spec.rb +47 -0
- data/spec/controllers/admin/health_controller_spec.rb +49 -0
- data/spec/controllers/admin/pages_controller_spec.rb +136 -0
- data/spec/controllers/admin/posts_controller_spec.rb +183 -0
- data/spec/controllers/admin/undo_items_controller_spec.rb +93 -0
- data/spec/controllers/archives_controller_spec.rb +37 -0
- data/spec/controllers/comments_controller_spec.rb +126 -0
- data/spec/controllers/pages_controller_spec.rb +46 -0
- data/spec/controllers/posts_controller_spec.rb +168 -0
- data/spec/dummy/Gemfile +5 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/config/application.rb +34 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +6 -0
- data/spec/dummy/config/enki.yml +20 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +34 -0
- data/spec/dummy/config/environments/test.rb +32 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +14 -0
- data/spec/dummy/config.ru +6 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/factories/factories.rb +36 -0
- data/spec/helpers/page_title_helper_spec.rb +54 -0
- data/spec/helpers/url_helper_spec.rb +23 -0
- data/spec/lib/slugorize_spec.rb +44 -0
- data/spec/models/comment_activity_spec.rb +60 -0
- data/spec/models/comment_spec.rb +125 -0
- data/spec/models/delete_comment_undo_spec.rb +52 -0
- data/spec/models/delete_post_undo_spec.rb +18 -0
- data/spec/models/page_spec.rb +75 -0
- data/spec/models/post_spec.rb +257 -0
- data/spec/models/stats_spec.rb +28 -0
- data/spec/models/tag_spec.rb +13 -0
- data/spec/models/tagging_spec.rb +30 -0
- data/spec/rcov.opts +2 -0
- data/spec/routing/admin/pages_routing_spec.rb +29 -0
- data/spec/routing/archives_routing_spec.rb +9 -0
- data/spec/routing/comments_routing_spec.rb +17 -0
- data/spec/routing/pages_routing_spec.rb +9 -0
- data/spec/routing/posts_routing_spec.rb +26 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +60 -0
- data/spec/support/be_valid_html5.rb +150 -0
- data/spec/support/be_valid_xhtml.rb +148 -0
- data/spec/support/routes_override_helper.rb +12 -0
- data/spec/views/admin/comments/index.html_spec.rb +26 -0
- data/spec/views/admin/comments/show.html_spec.rb +28 -0
- data/spec/views/admin/dashboard/show.html_spec.rb +37 -0
- data/spec/views/admin/pages/index.html_spec.rb +23 -0
- data/spec/views/admin/pages/new.html_spec.rb +16 -0
- data/spec/views/admin/pages/show.html_spec.rb +16 -0
- data/spec/views/admin/posts/index.html_spec.rb +24 -0
- data/spec/views/admin/posts/new.html_spec.rb +16 -0
- data/spec/views/admin/posts/show.html_spec.rb +16 -0
- data/spec/views/admin/undo_items/index.html_spec.rb +19 -0
- data/spec/views/archives/index.html_spec.rb +34 -0
- data/spec/views/pages/show.html_spec.rb +23 -0
- data/spec/views/posts/index.atom.builder_spec.rb +36 -0
- data/spec/views/posts/index.html_spec.rb +39 -0
- data/spec/views/posts/show.html_spec.rb +49 -0
- data/vendor/assets/javascripts/humanmsg.js +86 -0
- data/vendor/assets/javascripts/jquery.easing.1.3.js +205 -0
- data/vendor/assets/javascripts/jquery.form.js +869 -0
- data/vendor/assets/javascripts/jquery.jfeed.js +143 -0
- data/vendor/assets/javascripts/jquery.livequery.js +250 -0
- metadata +464 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
|
2
|
+
|
|
3
|
+
module Enki
|
|
4
|
+
|
|
5
|
+
describe Comment do
|
|
6
|
+
def valid_comment_attributes(extra = {})
|
|
7
|
+
{
|
|
8
|
+
:author => 'Don Alias',
|
|
9
|
+
:body => 'This is a comment',
|
|
10
|
+
:post => Post.new
|
|
11
|
+
}.merge(extra)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def set_comment_attributes(comment, extra = {})
|
|
15
|
+
valid_comment_attributes(extra).each_pair do |key, value|
|
|
16
|
+
comment.send("#{key}=", value)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
before(:each) do
|
|
21
|
+
@comment = Comment.new
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "is invalid with no post" do
|
|
25
|
+
set_comment_attributes(@comment, :post => nil)
|
|
26
|
+
@comment.should_not be_valid
|
|
27
|
+
@comment.errors.should_not be_empty
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "is invalid with no body" do
|
|
31
|
+
set_comment_attributes(@comment, :body => '')
|
|
32
|
+
@comment.should_not be_valid
|
|
33
|
+
@comment.errors.should_not be_empty
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "is invalid with no author" do
|
|
37
|
+
set_comment_attributes(@comment, :author => '')
|
|
38
|
+
@comment.should_not be_valid
|
|
39
|
+
@comment.errors.should_not be_empty
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "is valid with a full set of valid attributes" do
|
|
43
|
+
set_comment_attributes(@comment)
|
|
44
|
+
@comment.should be_valid
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "asks post to update it's comment counter after save" do
|
|
48
|
+
set_comment_attributes(@comment)
|
|
49
|
+
@comment.post.update_attributes(:title => 'My Post', :body => "body")
|
|
50
|
+
@comment.post.save
|
|
51
|
+
@comment.save
|
|
52
|
+
@comment.post.approved_comments.count.should == 1
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "asks post to update it's comment counter after destroy" do
|
|
56
|
+
set_comment_attributes(@comment)
|
|
57
|
+
@comment.post.update_attributes(:title => 'My Post', :body => "body")
|
|
58
|
+
@comment.post.save
|
|
59
|
+
@comment.save
|
|
60
|
+
@comment.destroy
|
|
61
|
+
@comment.post.approved_comments.count.should == 0
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "applies a Lesstile filter to body and store it in body_html before save" do
|
|
65
|
+
set_comment_attributes(@comment)
|
|
66
|
+
@comment.post.update_attributes(:title => 'My Post', :body => "body")
|
|
67
|
+
@comment.post.save
|
|
68
|
+
@comment.save
|
|
69
|
+
@comment.body_html.should_not be_nil
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "responds to trusted_user? for defensio integration" do
|
|
73
|
+
@comment.respond_to?(:trusted_user?).should == true
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "responds to user_logged_in? for defensio integration" do
|
|
77
|
+
@comment.respond_to?(:user_logged_in?).should == true
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "delegates post_tile to post" do
|
|
81
|
+
@comment.post = mock_model(Post)
|
|
82
|
+
@comment.post.should_receive(:title).and_return("hello")
|
|
83
|
+
@comment.post_title.should == "hello"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# TODO: acts_as_defensio_comment tests
|
|
87
|
+
# TODO: OpenID error model
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
describe Comment, '.find_recent' do
|
|
91
|
+
it 'finds the most recent comments that were posted before now' do
|
|
92
|
+
now = Time.now
|
|
93
|
+
Time.stub!(:now).and_return(now)
|
|
94
|
+
Comment.should_receive(:find).with(:all, {
|
|
95
|
+
:order => 'created_at DESC',
|
|
96
|
+
:limit => Comment::DEFAULT_LIMIT
|
|
97
|
+
}).and_return(comments = [mock_model(Comment)])
|
|
98
|
+
Comment.find_recent.should == comments
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it 'allows and override of the default limit' do
|
|
102
|
+
Comment.should_receive(:find).with(:all, hash_including(:limit => 999))
|
|
103
|
+
Comment.find_recent(:limit => 999)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
describe Comment, '.build_for_preview' do
|
|
108
|
+
before(:each) do
|
|
109
|
+
@comment = Comment.build_for_preview(:author => 'Don Alias', :body => 'A Comment')
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
it 'returns a new comment' do
|
|
113
|
+
@comment.should be_new_record
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it 'sets created_at' do
|
|
117
|
+
@comment.created_at.should_not be_nil
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it 'applies filter to body' do
|
|
121
|
+
@comment.body_html.should == 'A Comment'
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Enki
|
|
4
|
+
|
|
5
|
+
describe DeleteCommentUndo do
|
|
6
|
+
describe '#process!' do
|
|
7
|
+
it 'creates a new comment based on the attributes stored in #data' do
|
|
8
|
+
Comment.stub!(:find_by_id).and_return(nil)
|
|
9
|
+
|
|
10
|
+
item = DeleteCommentUndo.new(:data => "---\nid: 1\na: b")
|
|
11
|
+
item.stub!(:transaction).and_yield
|
|
12
|
+
item.stub!(:destroy)
|
|
13
|
+
|
|
14
|
+
Comment.should_receive(:create).with('a' => 'b').and_return(mock("comment", :new_record? => false))
|
|
15
|
+
item.process!
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
describe '#process! with existing comment' do
|
|
20
|
+
it 'raises' do
|
|
21
|
+
Comment.stub!(:find_by_id).and_return(Object.new)
|
|
22
|
+
lambda { DeleteCommentUndo.new(:data => "---\nid: 1").process! }.should raise_error(UndoFailed)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe '#process! with invalid comment' do
|
|
27
|
+
it 'raises' do
|
|
28
|
+
Comment.stub!(:find_by_id).and_return(nil)
|
|
29
|
+
|
|
30
|
+
Comment.stub!(:create).and_return(mock("comment", :new_record? => true))
|
|
31
|
+
lambda { DeleteCommentUndo.new(:data => "---\nid: 1").process! }.should raise_error(UndoFailed)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe '#description' do
|
|
36
|
+
it("should not be nil") { DeleteCommentUndo.new(:data => '---').description.should_not be_nil }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe '#complete_description' do
|
|
40
|
+
it("should not be nil") { DeleteCommentUndo.new(:data => '---').complete_description.should_not be_nil }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe '.create_undo' do
|
|
44
|
+
it "creates a new undo item based on the attributes of the given comment" do
|
|
45
|
+
comment = Comment.new(:author => 'Don Alias')
|
|
46
|
+
DeleteCommentUndo.should_receive(:create!).with(:data => comment.attributes.to_yaml).and_return(obj = Object.new)
|
|
47
|
+
DeleteCommentUndo.create_undo(comment).should == obj
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Enki
|
|
4
|
+
|
|
5
|
+
describe DeletePostUndo do
|
|
6
|
+
describe '#process!' do
|
|
7
|
+
it 'creates a new post with comments based on the attributes stored in #data' do
|
|
8
|
+
post = Post.create!(:title => 'a', :body => 'b').tap do |post|
|
|
9
|
+
post.comments.create!(:author => 'Don', :author_url => '', :author_email => '', :body => 'comment')
|
|
10
|
+
end
|
|
11
|
+
item = post.destroy_with_undo
|
|
12
|
+
new_post = item.process!
|
|
13
|
+
new_post.comments.count.should == 1
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
|
2
|
+
|
|
3
|
+
module Enki
|
|
4
|
+
|
|
5
|
+
describe Page, '#generate_slug' do
|
|
6
|
+
it 'makes a slug from the title if slug if blank' do
|
|
7
|
+
page = Page.new(:slug => '', :title => 'my title')
|
|
8
|
+
page.generate_slug
|
|
9
|
+
page.slug.should == 'my-title'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'replaces & with and' do
|
|
13
|
+
page = Page.new(:slug => 'a & b & c')
|
|
14
|
+
page.generate_slug
|
|
15
|
+
page.slug.should == 'a-and-b-and-c'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'replaces non alphanumeric characters with -' do
|
|
19
|
+
page = Page.new(:slug => 'a@#^*(){}b')
|
|
20
|
+
page.generate_slug
|
|
21
|
+
page.slug.should == 'a-b'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'does not modify title' do
|
|
25
|
+
page = Page.new(:title => 'My Page')
|
|
26
|
+
page.generate_slug
|
|
27
|
+
page.title.should == 'My Page'
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe Page, 'before validation' do
|
|
32
|
+
it 'calls #generate_slug' do
|
|
33
|
+
page = Page.new(:title => "My Page", :body => "body")
|
|
34
|
+
page.valid?
|
|
35
|
+
page.slug.should_not be_blank
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe Page, 'validations' do
|
|
40
|
+
def valid_page_attributes
|
|
41
|
+
{
|
|
42
|
+
:title => "My Page",
|
|
43
|
+
:slug => "my-page",
|
|
44
|
+
:body => "body"
|
|
45
|
+
}
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'is valid with valid_page_attributes' do
|
|
49
|
+
Page.new(valid_page_attributes).should be_valid
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it 'is invalid with no title' do
|
|
53
|
+
Page.new(valid_page_attributes.merge(:title => '')).should_not be_valid
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it 'is invalid with no body' do
|
|
57
|
+
Page.new(valid_page_attributes.merge(:body => '')).should_not be_valid
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
describe Page, '.build_for_preview' do
|
|
62
|
+
before(:each) do
|
|
63
|
+
@page = Page.build_for_preview(:title => 'My Page', :body => "body")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it 'returns a new page' do
|
|
67
|
+
@page.should be_new_record
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it 'applies filter to body' do
|
|
71
|
+
@page.body_html.should == '<p>body</p>'
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
end
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Enki
|
|
4
|
+
|
|
5
|
+
describe "Scopes" do
|
|
6
|
+
|
|
7
|
+
describe "find_recent" do
|
|
8
|
+
|
|
9
|
+
before do
|
|
10
|
+
Base::Post.send(:remove_const, :DEFAULT_LIMIT)
|
|
11
|
+
Base::Post::DEFAULT_LIMIT = 3
|
|
12
|
+
|
|
13
|
+
now = Time.now
|
|
14
|
+
Time.stub!(:now).and_return(now)
|
|
15
|
+
|
|
16
|
+
@post1 = create(:post, :published_at => 1.day.ago)
|
|
17
|
+
@post2 = create(:post, :published_at => 1.month.ago, :tag_list => 'yikes')
|
|
18
|
+
@post3 = create(:post, :published_at => 1.week.ago)
|
|
19
|
+
@post4 = create(:post, :published_at => 1.year.ago, :tag_list => 'yikes')
|
|
20
|
+
2.times { create(:post, :published_at => 1.days.since) }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'finds the most recent posts that were published before now' do
|
|
24
|
+
Post.find_recent.should == [@post1, @post3, @post2]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'finds the most recent posts that were published before now with a tag' do
|
|
28
|
+
Post.find_recent(:tag => 'yikes').should == [@post2, @post4]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe Post, "integration" do
|
|
36
|
+
describe 'setting tag_list' do
|
|
37
|
+
it 'increments tag counter cache' do
|
|
38
|
+
post1 = Post.create!(:title => 'My Post', :body => "body", :tag_list => "ruby")
|
|
39
|
+
post2 = Post.create!(:title => 'My Post', :body => "body", :tag_list => "ruby")
|
|
40
|
+
Tag.find_by_name('ruby').taggings_count.should == 2
|
|
41
|
+
Post.last.destroy
|
|
42
|
+
Tag.find_by_name('ruby').taggings_count.should == 1
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
describe Post, ".find_recent" do
|
|
48
|
+
|
|
49
|
+
it 'finds all posts grouped by month' do
|
|
50
|
+
now = Time.now
|
|
51
|
+
Time.stub!(:now).and_return(now)
|
|
52
|
+
posts = [1, 1, 2].collect {|month| mock_model(Post, :month => month) }
|
|
53
|
+
Post.should_receive(:find).with(:all, {
|
|
54
|
+
:order => 'posts.published_at DESC',
|
|
55
|
+
:conditions => ['published_at < ?', now]
|
|
56
|
+
}).and_return(posts)
|
|
57
|
+
months = Post.find_all_grouped_by_month.collect {|month| [month.date, month.posts]}
|
|
58
|
+
months.should == [[1, [posts[0], posts[1]]], [2, [posts[2]]]]
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
describe Post, '#generate_slug' do
|
|
63
|
+
it 'makes a slug from the title if slug if blank' do
|
|
64
|
+
post = Post.new(:slug => '', :title => 'my title')
|
|
65
|
+
post.generate_slug
|
|
66
|
+
post.slug.should == 'my-title'
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'replaces & with and' do
|
|
70
|
+
post = Post.new(:slug => 'a & b & c')
|
|
71
|
+
post.generate_slug
|
|
72
|
+
post.slug.should == 'a-and-b-and-c'
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it 'replaces non alphanumeric characters with -' do
|
|
76
|
+
post = Post.new(:slug => 'a@#^*(){}b')
|
|
77
|
+
post.generate_slug
|
|
78
|
+
post.slug.should == 'a-b'
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it 'does not modify title' do
|
|
82
|
+
post = Post.new(:title => 'My Post')
|
|
83
|
+
post.generate_slug
|
|
84
|
+
post.title.should == 'My Post'
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
describe Post, '#tag_list=' do
|
|
89
|
+
it 'accept an array argument so it is symmetrical with the reader' do
|
|
90
|
+
p = Post.new
|
|
91
|
+
p.tag_list = ["a", "b"]
|
|
92
|
+
p.tag_list.should == ["a", "b"]
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
describe Post, "#set_dates" do
|
|
97
|
+
describe 'when minor_edit is false' do
|
|
98
|
+
it 'sets edited_at to current time' do
|
|
99
|
+
now = Time.now
|
|
100
|
+
Time.stub!(:now).and_return(now)
|
|
101
|
+
|
|
102
|
+
post = Post.new(:edited_at => 1.day.ago)
|
|
103
|
+
post.stub!(:minor_edit?).and_return(false)
|
|
104
|
+
post.set_dates
|
|
105
|
+
post.edited_at.should == now
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
describe 'when edited_at is nil' do
|
|
110
|
+
it 'sets edited_at to current time' do
|
|
111
|
+
now = Time.now
|
|
112
|
+
Time.stub!(:now).and_return(now)
|
|
113
|
+
|
|
114
|
+
post = Post.new
|
|
115
|
+
post.stub!(:minor_edit?).and_return(true)
|
|
116
|
+
post.set_dates
|
|
117
|
+
post.edited_at.should == now
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
describe 'when minor_edit is true' do
|
|
122
|
+
it 'does not changed edited_at' do
|
|
123
|
+
post = Post.new(:edited_at => now = 1.day.ago)
|
|
124
|
+
post.stub!(:minor_edit?).and_return(true)
|
|
125
|
+
post.set_dates
|
|
126
|
+
post.edited_at.should == now
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it 'sets published_at by parsing published_at_natural with chronic' do
|
|
131
|
+
now = Time.now
|
|
132
|
+
post = Post.new(:published_at_natural => 'now')
|
|
133
|
+
Chronic.should_receive(:parse).with('now').and_return(now)
|
|
134
|
+
post.set_dates
|
|
135
|
+
post.published_at.should == now
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it 'does not set published_at if published_at_natural is invalid' do
|
|
139
|
+
now = Time.now
|
|
140
|
+
post = Post.new(:published_at_natural => 'bogus', :published_at => now)
|
|
141
|
+
Chronic.should_receive(:parse).with('bogus').and_return(nil)
|
|
142
|
+
post.set_dates
|
|
143
|
+
post.published_at.should == now
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
describe Post, "#minor_edit" do
|
|
148
|
+
it('returns "1" by default') { Post.new.minor_edit.should == "1" }
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
describe Post, '#published?' do
|
|
152
|
+
before(:each) do
|
|
153
|
+
@post = Post.new
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it "should return false if published_at is not filled" do
|
|
157
|
+
@post.should_not be_published
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it "should return true if published_at is filled" do
|
|
161
|
+
@post.published_at = Time.now
|
|
162
|
+
@post.should be_published
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
describe Post, "#minor_edit?" do
|
|
167
|
+
it('returns true when minor_edit is 1') { Post.new(:minor_edit => "1").minor_edit?.should == true }
|
|
168
|
+
it('returns false when minor_edit is 0') { Post.new(:minor_edit => "0").minor_edit?.should == false }
|
|
169
|
+
it('returns true by default') { Post.new.minor_edit?.should == true }
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
describe Post, 'before validation' do
|
|
173
|
+
it 'calls #generate_slug' do
|
|
174
|
+
post = Post.new(:title => "My Post", :body => "body")
|
|
175
|
+
post.valid?
|
|
176
|
+
post.slug.should_not be_blank
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
it 'calls #set_dates' do
|
|
180
|
+
post = Post.new(:title => "My Post", :body => "body")
|
|
181
|
+
post.valid?
|
|
182
|
+
post.edited_at.should_not be_blank
|
|
183
|
+
post.published_at.should_not be_blank
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
describe Post, '#denormalize_comments_count!' do
|
|
188
|
+
it 'updates approved_comments_count without triggering AR callbacks' do
|
|
189
|
+
p = Post.new
|
|
190
|
+
p.id = 999
|
|
191
|
+
p.stub!(:approved_comments).and_return(stub("approved_comments association", :count => 9))
|
|
192
|
+
Base::Post.should_receive(:update_all).with(["approved_comments_count = ?", 9], ["id = ?", 999])
|
|
193
|
+
p.denormalize_comments_count!
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
describe Post, 'validations' do
|
|
198
|
+
def valid_post_attributes
|
|
199
|
+
{
|
|
200
|
+
:title => "My Post",
|
|
201
|
+
:slug => "my-post",
|
|
202
|
+
:body => "hello this is my post",
|
|
203
|
+
:published_at_natural => 'now'
|
|
204
|
+
}
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
it 'is valid with valid_post_attributes' do
|
|
208
|
+
Post.new(valid_post_attributes).should be_valid
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
it 'is invalid with no title' do
|
|
212
|
+
Post.new(valid_post_attributes.merge(:title => '')).should_not be_valid
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
it 'is invalid with no body' do
|
|
216
|
+
Post.new(valid_post_attributes.merge(:body => '')).should_not be_valid
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
it 'is invalid with bogus published_at_natural' do
|
|
220
|
+
Post.new(valid_post_attributes.merge(:published_at_natural => 'bogus')).should_not be_valid
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
describe Post, 'being destroyed' do
|
|
225
|
+
it 'destroys all comments' do
|
|
226
|
+
Post.reflect_on_association(:comments).options[:dependent].should == :destroy
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
describe Post, '.build_for_preview' do
|
|
231
|
+
before(:each) do
|
|
232
|
+
@post = Post.build_for_preview(:title => 'My Post', :body => "body", :tag_list => "ruby")
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
it 'returns a new post' do
|
|
236
|
+
@post.should be_new_record
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
it 'generates slug' do
|
|
240
|
+
@post.slug.should_not be_nil
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
it 'sets date' do
|
|
244
|
+
@post.edited_at.should_not be_nil
|
|
245
|
+
@post.published_at.should_not be_nil
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
it 'applies filter to body' do
|
|
249
|
+
@post.body_html.should == '<p>body</p>'
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
it 'generates tags from tag_list' do
|
|
253
|
+
@post.tags.collect {|tag| tag.name}.should == ['ruby']
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Enki
|
|
4
|
+
|
|
5
|
+
describe Stats do
|
|
6
|
+
describe '#post_count' do
|
|
7
|
+
it 'returns the total number of posts, published or not' do
|
|
8
|
+
Post.should_receive(:count).and_return(2)
|
|
9
|
+
Stats.new.post_count.should == 2
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe '#comment_count' do
|
|
14
|
+
it 'returns the total number of comments' do
|
|
15
|
+
Comment.should_receive(:count).and_return(2)
|
|
16
|
+
Stats.new.comment_count.should == 2
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe '#tag_count' do
|
|
21
|
+
it 'returns the total number of tags' do
|
|
22
|
+
Tag.should_receive(:count).and_return(2)
|
|
23
|
+
Stats.new.tag_count.should == 2
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
|
2
|
+
|
|
3
|
+
module Enki
|
|
4
|
+
|
|
5
|
+
describe Tag do
|
|
6
|
+
describe 'on delete' do
|
|
7
|
+
it 'also deletes all associated taggings' do
|
|
8
|
+
Tag.reflect_on_association(:taggings).options[:dependent].should == :destroy
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
|
2
|
+
|
|
3
|
+
module Enki
|
|
4
|
+
|
|
5
|
+
describe Tagging do
|
|
6
|
+
before(:each) do
|
|
7
|
+
@taggable = Post.create!(:title => 'My Post', :body => 'body', :tag_list => 'oblong, square, triangle')
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it 'destroys unused tags on taggable update' do
|
|
11
|
+
@taggable.tag_list = ''
|
|
12
|
+
@taggable.save
|
|
13
|
+
Tag.where(:taggings_count => 0).count.should == 0
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'destroys unused tags on taggable destroy' do
|
|
17
|
+
Enki::Tag.remove_unused = true
|
|
18
|
+
|
|
19
|
+
@taggable.destroy
|
|
20
|
+
Tag.where(:taggings_count => 0).count.should == 0
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'does not destroy tags if they are still in use' do
|
|
24
|
+
another_taggable = Post.create!(:title => 'My Post', :body => 'body', :tag_list => 'oblong, square')
|
|
25
|
+
@taggable.destroy
|
|
26
|
+
Tag.where(:name => ['oblong', 'square']).count.should == 2
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
data/spec/rcov.opts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'Enki::Admin::PagesController routes' do
|
|
4
|
+
|
|
5
|
+
it "routes get /admin/pages to admin/pages#create" do
|
|
6
|
+
get("/admin/pages").should route_to('enki/admin/pages#index')
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it "routes post /admin/pages to admin/pages#create" do
|
|
10
|
+
post("/admin/pages").should route_to('enki/admin/pages#create')
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "routes get /admin/pages/new to admin/pages#new" do
|
|
14
|
+
get("/admin/pages/new").should route_to('enki/admin/pages#new')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "routes get /admin/pages/my-page to admin/pages#show id: my-page" do
|
|
18
|
+
get("/admin/pages/my-page").should route_to('enki/admin/pages#show', :id => 'my-page')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "routes put /admin/pages/my-page to admin/pages#update id: my-page" do
|
|
22
|
+
put("admin/pages/my-page").should route_to('enki/admin/pages#update', :id => 'my-page')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "routes delete /admin/pages/my-page to admin/pages#destroy id: my-page" do
|
|
26
|
+
delete("/admin/pages/my-page").should route_to('enki/admin/pages#destroy', :id => 'my-page')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'CommentsController routes' do
|
|
4
|
+
|
|
5
|
+
it "should route get /2008/02/01/a-post/comments to comments#index with year: 2008, month: 02, day: 01, slug: a-post" do
|
|
6
|
+
get("/2008/02/01/a-post/comments").should route_to("enki/comments#index", :year => '2008', :month => '02', :day => '01', :slug => 'a-post')
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it "should route post /2008/02/01/a-post/comments to comments#create with year: 2008, month: 02, day: 01, slug: a-post" do
|
|
10
|
+
post("/2008/02/01/a-post/comments").should route_to("enki/comments#create", :year => '2008', :month => '02', :day => '01', :slug => 'a-post')
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "should route /2008/02/01/a-post/comments/nes to comments#new with year: 2008, month: 02, day: 01, slug: a-post" do
|
|
14
|
+
get("/2008/02/01/a-post/comments/new").should route_to("enki/comments#new", :year => '2008', :month => '02', :day => '01', :slug => 'a-post')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|