mist 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.rspec +1 -0
- data/.rvmrc +1 -0
- data/.travis.yml +13 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +192 -0
- data/README.md +108 -0
- data/Rakefile +9 -0
- data/app/assets/images/mist/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
- data/app/assets/images/mist/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
- data/app/assets/images/mist/ui-bg_flat_10_000000_40x100.png +0 -0
- data/app/assets/images/mist/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
- data/app/assets/images/mist/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
- data/app/assets/images/mist/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/app/assets/images/mist/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
- data/app/assets/images/mist/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
- data/app/assets/images/mist/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
- data/app/assets/images/mist/ui-icons_222222_256x240.png +0 -0
- data/app/assets/images/mist/ui-icons_228ef1_256x240.png +0 -0
- data/app/assets/images/mist/ui-icons_ef8c08_256x240.png +0 -0
- data/app/assets/images/mist/ui-icons_ffd27a_256x240.png +0 -0
- data/app/assets/images/mist/ui-icons_ffffff_256x240.png +0 -0
- data/app/assets/javascripts/mist/jquery-ui-1.8.17.custom.min.js +356 -0
- data/app/assets/javascripts/mist/posts.js.coffee +3 -0
- data/app/assets/javascripts/mist_core.js +3 -0
- data/app/assets/stylesheets/mist/posts.css.sass +97 -0
- data/app/assets/stylesheets/mist/scaffolds.css.scss +56 -0
- data/app/assets/stylesheets/mist/ui-lightness/jquery-ui-1.8.17.custom.css +565 -0
- data/app/assets/stylesheets/mist_core.css +4 -0
- data/app/controllers/application_controller.rb +3 -0
- data/app/controllers/mist/posts_controller.rb +130 -0
- data/app/helpers/mist/posts_helper.rb +61 -0
- data/app/mailers/.gitkeep +0 -0
- data/app/models/.gitkeep +0 -0
- data/app/models/mist/post.rb +240 -0
- data/app/models/mist/post_sweeper.rb +63 -0
- data/app/views/layouts/mist/posts.html.erb +66 -0
- data/app/views/mist/posts/_form.html.erb +55 -0
- data/app/views/mist/posts/_post.html.erb +19 -0
- data/app/views/mist/posts/_sidebar_title.html.erb +5 -0
- data/app/views/mist/posts/edit.html.erb +16 -0
- data/app/views/mist/posts/feed.atom.builder +17 -0
- data/app/views/mist/posts/index.html.erb +31 -0
- data/app/views/mist/posts/new.html.erb +15 -0
- data/app/views/mist/posts/show.html.erb +3 -0
- data/config/cucumber.yml +8 -0
- data/config/environment.rb +1 -0
- data/config/routes.rb +7 -0
- data/features/atom_feed.feature +20 -0
- data/features/authorize_create_post.feature +14 -0
- data/features/authorize_destroy_post.feature +27 -0
- data/features/authorize_update_post.feature +28 -0
- data/features/authorize_view_unpublished.feature +22 -0
- data/features/create_post.feature +13 -0
- data/features/destroy_post.feature +13 -0
- data/features/format_with_markdown.feature +13 -0
- data/features/popular_posts.feature +35 -0
- data/features/recent_posts.feature +28 -0
- data/features/similar_posts.feature +43 -0
- data/features/step_definitions/authorization_steps.rb +17 -0
- data/features/step_definitions/filesystem_steps.rb +13 -0
- data/features/step_definitions/page_steps.rb +43 -0
- data/features/step_definitions/post_steps.rb +58 -0
- data/features/step_definitions/sidebar_steps.rb +21 -0
- data/features/support/env.rb +58 -0
- data/features/support/setup.rb +15 -0
- data/features/update_post.feature +19 -0
- data/gemfiles/common +20 -0
- data/gemfiles/rails-3.1.3 +4 -0
- data/gemfiles/rails-3.1.3.lock +195 -0
- data/gemfiles/rails-3.2.0 +4 -0
- data/lib/generators/mist/USAGE +13 -0
- data/lib/generators/mist/setup_generator.rb +21 -0
- data/lib/generators/mist/templates/initializer.rb +17 -0
- data/lib/generators/mist/templates/mist.css.scss +2 -0
- data/lib/generators/mist/templates/mist.js.coffee +1 -0
- data/lib/generators/mist/views_generator.rb +11 -0
- data/lib/mist.rb +26 -0
- data/lib/mist/code_example_parser.rb +86 -0
- data/lib/mist/configuration.rb +68 -0
- data/lib/mist/configuration/author.rb +8 -0
- data/lib/mist/engine.rb +12 -0
- data/lib/mist/git_file_system_history.rb +66 -0
- data/lib/mist/git_model.rb +154 -0
- data/lib/mist/git_model/attributes.rb +21 -0
- data/lib/mist/git_model/class_methods.rb +141 -0
- data/lib/mist/permalink.rb +5 -0
- data/lib/mist/repository.rb +15 -0
- data/lib/mist/version.rb +8 -0
- data/mist.gemspec +33 -0
- data/spec/controllers/posts_controller_spec.rb +263 -0
- data/spec/dummy_rails_app/app/assets/javascripts/mist.js.coffee +1 -0
- data/spec/dummy_rails_app/app/assets/stylesheets/mist.css.scss +2 -0
- data/spec/dummy_rails_app/app/views/layouts/mist/posts.html.erb +60 -0
- data/spec/dummy_rails_app/config.ru +4 -0
- data/spec/dummy_rails_app/config/application.rb +48 -0
- data/spec/dummy_rails_app/config/boot.rb +7 -0
- data/spec/dummy_rails_app/config/database.yml +28 -0
- data/spec/dummy_rails_app/config/environment.rb +5 -0
- data/spec/dummy_rails_app/config/environments/development.rb +32 -0
- data/spec/dummy_rails_app/config/environments/production.rb +60 -0
- data/spec/dummy_rails_app/config/environments/test.rb +42 -0
- data/spec/dummy_rails_app/config/initializers/active_gist_credentials.rb +8 -0
- data/spec/dummy_rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy_rails_app/config/initializers/inflections.rb +10 -0
- data/spec/dummy_rails_app/config/initializers/mime_types.rb +5 -0
- data/spec/dummy_rails_app/config/initializers/mist.rb +17 -0
- data/spec/dummy_rails_app/config/initializers/secret_token.rb +7 -0
- data/spec/dummy_rails_app/config/initializers/session_store.rb +8 -0
- data/spec/dummy_rails_app/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy_rails_app/config/locales/en.yml +5 -0
- data/spec/dummy_rails_app/config/routes.rb +3 -0
- data/spec/dummy_rails_app/db/schema.rb +22 -0
- data/spec/dummy_rails_app/db/seeds.rb +7 -0
- data/spec/dummy_rails_app/doc/README_FOR_APP +2 -0
- data/spec/dummy_rails_app/lib/assets/.gitkeep +0 -0
- data/spec/dummy_rails_app/lib/tasks/.gitkeep +0 -0
- data/spec/dummy_rails_app/lib/tasks/cucumber.rake +65 -0
- data/spec/dummy_rails_app/public/404.html +26 -0
- data/spec/dummy_rails_app/public/422.html +26 -0
- data/spec/dummy_rails_app/public/500.html +26 -0
- data/spec/dummy_rails_app/public/favicon.ico +0 -0
- data/spec/dummy_rails_app/public/robots.txt +5 -0
- data/spec/dummy_rails_app/script/cucumber +10 -0
- data/spec/dummy_rails_app/script/rails +6 -0
- data/spec/dummy_rails_app/vendor/assets/stylesheets/.gitkeep +0 -0
- data/spec/dummy_rails_app/vendor/plugins/.gitkeep +0 -0
- data/spec/factories/posts.rb +8 -0
- data/spec/fixtures/gist_404 +6 -0
- data/spec/fixtures/gist_with_1_code_example +66 -0
- data/spec/helpers/posts_helper_spec.rb +64 -0
- data/spec/lib/mist/code_example_parser_spec.rb +135 -0
- data/spec/lib/mist/configuration_spec.rb +88 -0
- data/spec/lib/mist/permalink_spec.rb +17 -0
- data/spec/lib/mist/repository_spec.rb +20 -0
- data/spec/models/mist/git_model_spec.rb +260 -0
- data/spec/models/mist/post_spec.rb +575 -0
- data/spec/requests/posts_caching_spec.rb +152 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/cache_helpers.rb +71 -0
- data/spec/support/config.rb +42 -0
- data/spec/support/fakeweb.rb +3 -0
- data/spec/views/mist/posts/edit.html.erb_spec.rb +11 -0
- data/spec/views/mist/posts/index.html.erb_spec.rb +27 -0
- data/spec/views/mist/posts/new.html.erb_spec.rb +15 -0
- data/spec/views/mist/posts/show.html.erb_spec.rb +11 -0
- metadata +371 -0
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Mist::Configuration do
|
4
|
+
subject { Class.new { include Mist::Configuration }.new }
|
5
|
+
|
6
|
+
it "should have a default repo location" do
|
7
|
+
subject.repository_location.should_not be_blank
|
8
|
+
subject.repository_location.should == subject.default_repository_location
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should allow repo location to be set" do
|
12
|
+
subject.repository_location = "123"
|
13
|
+
subject.repository_location.should == '123'
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "authorization" do
|
17
|
+
it "should accept multiple arguments" do
|
18
|
+
Mist.authorize(:one, :two) { true }
|
19
|
+
Mist.should be_authorized(:one)
|
20
|
+
Mist.should be_authorized(:two)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should work default to :all given no arguments" do
|
24
|
+
Mist.authorize { true }
|
25
|
+
Mist.should be_authorized(:create_post)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should require a block to set up" do
|
29
|
+
proc { Mist.authorize(:test) }.should raise_error(ArgumentError)
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "with authorization for a single action" do
|
33
|
+
before { Mist.authorize(:test) { |*c| @received = c; true } }
|
34
|
+
|
35
|
+
it "should return the block result" do
|
36
|
+
Mist.should be_authorized(:test)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should pass extra arguments to the block" do
|
40
|
+
Mist.authorized?(:test, 1, 2, 3)
|
41
|
+
@received.should == [1, 2, 3]
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should return false for other actions" do
|
45
|
+
Mist.should_not be_authorized(:other)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should reset" do
|
49
|
+
Mist.reset_authorizations!
|
50
|
+
Mist.should_not be_authorized(:test)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "with authorization for all actions" do
|
55
|
+
before { Mist.authorize(:all) { true } }
|
56
|
+
|
57
|
+
it "should return block result for any action" do
|
58
|
+
Mist.should be_authorized(:test)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "by default" do
|
63
|
+
it "should deny authorization for all actions" do
|
64
|
+
Mist.should_not be_authorized(:test)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "overriding all actions with a given action" do
|
69
|
+
before { Mist.authorize(:all) { true }; Mist.authorize(:test) { false } }
|
70
|
+
|
71
|
+
it "should return overridden result for the overridden action" do
|
72
|
+
Mist.should_not be_authorized(:test)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should not return overridden result for other actions" do
|
76
|
+
Mist.should be_authorized(:other)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "overriding the same action" do
|
81
|
+
before { Mist.authorize(:test) { false }; Mist.authorize(:test) { true } }
|
82
|
+
|
83
|
+
it "should use the last block given" do
|
84
|
+
Mist.should be_authorized(:test)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Mist::Permalink do
|
4
|
+
include Mist::Permalink
|
5
|
+
|
6
|
+
it "should not include periods" do # they screw with rails' formats
|
7
|
+
permalink("Stuck in Rails 2? Use Bundler. For everything. Right now.").should_not match(/\./)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should not create double dashes" do # they're ugly
|
11
|
+
permalink("Stuck in Rails 2? Use Bundler. For everything. Right now.").should_not match(/--/)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should not end with dashes" do # ditto
|
15
|
+
permalink("Stuck in Rails 2? Use Bundler. For everything. Right now.").should_not match(/-$/)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Mist do
|
4
|
+
it "should generate the repo automatically" do
|
5
|
+
Mist.repository
|
6
|
+
File.should be_directory(Mist.repository_location.join('.git'))
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "with an existing repo" do
|
10
|
+
before do
|
11
|
+
Git.init(Mist.repository_location.to_s)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should not generate the repo" do
|
15
|
+
Git.should_not_receive(:init)
|
16
|
+
|
17
|
+
Mist.repository
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,260 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mist/git_model'
|
3
|
+
|
4
|
+
describe Mist::GitModel do
|
5
|
+
let(:model_class) { Class.new(Mist::GitModel) { def self.name; "TestModel"; end } }
|
6
|
+
subject { model_class.new }
|
7
|
+
|
8
|
+
describe "when no records ever existed" do
|
9
|
+
it "last record should be nil" do
|
10
|
+
model_class.last.should be_nil
|
11
|
+
end
|
12
|
+
|
13
|
+
it "last 5 records should be empty" do
|
14
|
+
model_class.last(5).should be_empty
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "when 1 record exists" do
|
19
|
+
before { model_class.create! }
|
20
|
+
it "last record should be the record" do
|
21
|
+
model_class.last.should be_kind_of(model_class)
|
22
|
+
end
|
23
|
+
it "last 5 records should be 1-element" do
|
24
|
+
model_class.last(5).should have(1).record
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "when 2 records exist" do
|
29
|
+
before { model_class.create!; model_class.create! }
|
30
|
+
it "last record should be returned" do
|
31
|
+
model_class.last.should be_kind_of(model_class)
|
32
|
+
end
|
33
|
+
it 'last 5 records should contain 2' do
|
34
|
+
model_class.last(5).should have(2).records
|
35
|
+
end
|
36
|
+
it 'should return them in order, most recent first' do
|
37
|
+
model_class.last(5).collect { |i| i.id }.should == ['2', '1']
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "when 10 records exist" do
|
42
|
+
before { 10.times { model_class.create! } }
|
43
|
+
it "last 5 records should not exceed 5" do
|
44
|
+
model_class.last(5).should have(5).records
|
45
|
+
end
|
46
|
+
it 'should return them in order, most recent first' do
|
47
|
+
model_class.last(5).collect { |i| i.id }.should == ['10', '9', '8', '7', '6']
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should underscore and pluralize #table_name" do
|
52
|
+
subject.table_name.should == 'test_models'
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should not be changed" do
|
56
|
+
# applying defaults should not be considered a change to a new record
|
57
|
+
subject.should_not be_changed
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "validation" do
|
61
|
+
it "should be wrapped in callbacks" do
|
62
|
+
before = after = false
|
63
|
+
model_class.before_validation { before = true }
|
64
|
+
model_class.after_validation { after = true }
|
65
|
+
|
66
|
+
model_class.new.valid?
|
67
|
+
before.should be_true
|
68
|
+
after.should be_true
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should not require id" do
|
72
|
+
subject.valid?
|
73
|
+
subject.errors[:id].should_not include("can't be blank")
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "with an id" do
|
77
|
+
before { subject.id = 1 }
|
78
|
+
|
79
|
+
it "should enforce uniqueness of id" do
|
80
|
+
subject.save!
|
81
|
+
|
82
|
+
other = model_class.new(:id => 1)
|
83
|
+
other.valid?
|
84
|
+
other.errors[:id].should include("has already been taken")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "creation" do
|
90
|
+
subject { model_class.create! }
|
91
|
+
|
92
|
+
it "should return the model" do
|
93
|
+
subject.should be_kind_of(model_class)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should not be a new record" do
|
97
|
+
subject.should_not be_new_record
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should have an id" do
|
101
|
+
subject.id.should_not be_blank
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should find it by its id" do
|
105
|
+
model_class.find(subject.id).should == subject
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe "new default record" do
|
110
|
+
it { should_not be_persisted }
|
111
|
+
it { should be_new_record }
|
112
|
+
it { should_not be_changed }
|
113
|
+
|
114
|
+
describe "after saving" do
|
115
|
+
before { subject.save! }
|
116
|
+
|
117
|
+
it { should be_persisted }
|
118
|
+
it { should_not be_new_record }
|
119
|
+
it { should_not be_changed }
|
120
|
+
|
121
|
+
it "should increase count" do
|
122
|
+
model_class.count.should == 1
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should have created a file record" do
|
126
|
+
File.should be_file(subject.path)
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should have created a commit" do
|
130
|
+
Mist.log.size.should == 1
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should load the content in a separate query" do
|
134
|
+
model_class.find(subject.id).id.should == subject.id
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe "an existing record" do
|
140
|
+
subject { model_class.create! }
|
141
|
+
|
142
|
+
describe "altered on file system" do
|
143
|
+
it "should load id from filename, not from file contents" do
|
144
|
+
# create a tainted record directly on the filesystem
|
145
|
+
yaml = YAML.load(File.read(subject.path))
|
146
|
+
yaml['id'] = '2'
|
147
|
+
new_path = File.expand_path('3', File.dirname(subject.path))
|
148
|
+
File.open(new_path, "w") { |f| f.print yaml.to_yaml }
|
149
|
+
|
150
|
+
# now load it
|
151
|
+
model_class.find('3').id.should == '3'
|
152
|
+
end
|
153
|
+
|
154
|
+
describe "files renamed on file system but not committed to git" do
|
155
|
+
before do
|
156
|
+
FileUtils.mv subject.path, File.expand_path('2', File.dirname(subject.path))
|
157
|
+
model
|
158
|
+
end
|
159
|
+
|
160
|
+
let :model do
|
161
|
+
model = model_class.find('2')
|
162
|
+
model.save!
|
163
|
+
model
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should use the renamed file's id" do
|
167
|
+
model.id.should == '2'
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should commit the new file to git" do
|
171
|
+
File.should exist(File.expand_path('2', File.dirname(model.path)))
|
172
|
+
Mist.repository.lib.diff_files.should be_empty
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should remove the missing file from git" do
|
176
|
+
File.should_not exist(subject.path)
|
177
|
+
Mist.repository.lib.diff_files.should be_empty
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
describe "destroying" do
|
183
|
+
before { subject.destroy }
|
184
|
+
|
185
|
+
it "should not be found" do
|
186
|
+
model_class.find(subject.id).should be_nil
|
187
|
+
end
|
188
|
+
|
189
|
+
it "should produce a commit" do
|
190
|
+
Mist.log.size.should == 2
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should reduce count" do
|
194
|
+
model_class.count.should == 0
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
describe "with an attribute" do
|
199
|
+
before { model_class.attribute(:content); subject.save! }
|
200
|
+
|
201
|
+
describe "changing an attribute" do
|
202
|
+
before { subject.content = "changed"; subject.save! }
|
203
|
+
|
204
|
+
it "should create a commit" do
|
205
|
+
Mist.log.size.should == 2
|
206
|
+
end
|
207
|
+
|
208
|
+
it "should load the new content in a separate query" do
|
209
|
+
model_class.find(subject.id).content.should == "changed"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
describe "changing its id and its attribute simultaneously" do
|
214
|
+
before do
|
215
|
+
@old_path = subject.path
|
216
|
+
subject.id = 2
|
217
|
+
subject.content = "changed"
|
218
|
+
subject.save!
|
219
|
+
end
|
220
|
+
|
221
|
+
it "should create a commit" do
|
222
|
+
Mist.log.size.should == 2
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should have changed the content path" do
|
226
|
+
@old_path.should_not == subject.path
|
227
|
+
end
|
228
|
+
|
229
|
+
it "should have removed the old content path" do
|
230
|
+
File.should_not exist(@old_path)
|
231
|
+
end
|
232
|
+
|
233
|
+
it "should have created a new content path" do
|
234
|
+
File.should be_file(subject.path)
|
235
|
+
end
|
236
|
+
|
237
|
+
it "should load the new content in a separate query" do
|
238
|
+
model_class.find(subject.id).content.should == "changed"
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
|
245
|
+
describe "active model lint tests" do
|
246
|
+
include Test::Unit::Assertions
|
247
|
+
include ActiveModel::Lint::Tests
|
248
|
+
|
249
|
+
def model
|
250
|
+
subject
|
251
|
+
end
|
252
|
+
|
253
|
+
# to_s is to support ruby-1.9
|
254
|
+
ActiveModel::Lint::Tests.public_instance_methods.map{|m| m.to_s}.grep(/^test/).each do |m|
|
255
|
+
example m.gsub('_',' ') do
|
256
|
+
send m
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
@@ -0,0 +1,575 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Mist::Post do
|
4
|
+
it "preview" do
|
5
|
+
subject.content = "# This is a header and stuff\r\nHere's a paragraph, or it would be if
|
6
|
+
I had anything much to talk about, but it's really not right now because I'm busy.
|
7
|
+
Go away.\r\n\r\nHere's some sample code to keep you sated:\r\n\r\n file: test.rb\r\n
|
8
|
+
\ one = :one\r\n\r\n## and this is another header"
|
9
|
+
proc { subject.content_as_html_preview }.should_not raise_error
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should call observer" do
|
13
|
+
subject.title = subject.content = "title"
|
14
|
+
Mist::PostSweeper.instance.should_receive(:after_save)
|
15
|
+
subject.save
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "Mist::Post#matching_tags" do
|
19
|
+
before do
|
20
|
+
Mist::Post.create!(attributes_for(:post).merge(:title => 'one', :tags => 'one'))
|
21
|
+
Mist::Post.create!(attributes_for(:post).merge(:title => 'two', :tags => 'two'))
|
22
|
+
Mist::Post.create!(attributes_for(:post).merge(:title => 'four', :tags => nil))
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should return empty array if given nil" do
|
26
|
+
Mist::Post.matching_tags(nil).should be_empty
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should return empty array if given empty string" do
|
30
|
+
Mist::Post.matching_tags([""]).should be_empty
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should handle a single matching" do
|
34
|
+
Mist::Post.matching_tags(["one"]).should have(1).post
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should handle a single not matching" do
|
38
|
+
Mist::Post.matching_tags(["three"]).should be_empty
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should handle multiple matching" do
|
42
|
+
Mist::Post.matching_tags(['one', 'two']).should have(2).posts
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should handle multiple matching and not matching" do
|
46
|
+
Mist::Post.matching_tags(['one', 'three']).should have(1).post
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "tags" do
|
51
|
+
it "should save them" do
|
52
|
+
id = Mist::Post.create!(attributes_for(:post).merge(:tags => ['one', 'two', 'three'])).id
|
53
|
+
Mist::Post.find(id).tags.should == ['one', 'two', 'three']
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should split them out of a string" do
|
57
|
+
p = Mist::Post.new
|
58
|
+
p.tags = 'one, two three,four'
|
59
|
+
p.tags.should == ['one', 'two three', 'four']
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "recent posts" do
|
64
|
+
before do
|
65
|
+
@order = [ 1.day.ago, 2.days.ago, 3.days.ago, 4.days.ago, 5.days.ago ]
|
66
|
+
5.times { |i| Mist::Post.create!(:title => "title#{i}", :content => "content", :published_at => @order[i]) }
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should order by published_at descending" do
|
70
|
+
Mist::Post.recently_published(5)[0].published_at.should == @order[0]
|
71
|
+
Mist::Post.recently_published(5)[1].published_at.should == @order[1]
|
72
|
+
Mist::Post.recently_published(5)[2].published_at.should == @order[2]
|
73
|
+
Mist::Post.recently_published(5)[3].published_at.should == @order[3]
|
74
|
+
Mist::Post.recently_published(5)[4].published_at.should == @order[4]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "popularity" do
|
79
|
+
before do
|
80
|
+
subject.title = "post title"
|
81
|
+
subject.content = "content"
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should start at 0" do
|
85
|
+
subject.popularity.should == 0
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should not affect equality" do
|
89
|
+
# a post is equal if its ID matches, nothing else matters.
|
90
|
+
subject.save!
|
91
|
+
other = Mist::Post.last
|
92
|
+
|
93
|
+
subject.popularity = 1
|
94
|
+
subject.save!
|
95
|
+
|
96
|
+
subject.should == other
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should order by descending popularity" do
|
100
|
+
5.times { |i| Mist::Post.create!(:title => "title#{i}", :content => "content", :popularity => i) }
|
101
|
+
|
102
|
+
popular = Mist::Post.most_popular(5)
|
103
|
+
popular[0].popularity.should == 4
|
104
|
+
popular[1].popularity.should == 3
|
105
|
+
popular[2].popularity.should == 2
|
106
|
+
popular[3].popularity.should == 1
|
107
|
+
popular[4].popularity.should == 0
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "after saving" do
|
111
|
+
before { subject.save! }
|
112
|
+
|
113
|
+
it "should be included in popular posts" do
|
114
|
+
Mist::Post.most_popular(5).should include(subject)
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "and then deleting" do
|
118
|
+
before { subject.destroy }
|
119
|
+
|
120
|
+
it "should omit subject from popular posts" do
|
121
|
+
Mist::Post.most_popular(5).should be_empty
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe "after popularity has changed" do
|
126
|
+
before { subject.popularity = 5; subject.save! }
|
127
|
+
|
128
|
+
it "should load the popularity" do
|
129
|
+
Mist::Post.find(subject.id).popularity.should == 5
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should return the post as among the most popular" do
|
133
|
+
Mist::Post.most_popular(5).should include(subject)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should omit cr's" do
|
140
|
+
subject.content = "a\r\nb"
|
141
|
+
subject.content.should == "a\nb"
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should default to draft" do
|
145
|
+
subject.should_not be_published
|
146
|
+
subject.should be_draft
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should be published if publish date set" do
|
150
|
+
subject.published_at = Time.now
|
151
|
+
subject.should be_published
|
152
|
+
subject.should_not be_draft
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should be published now" do
|
156
|
+
time = Time.now
|
157
|
+
Time.stub!(:now).and_return(time) # to account for microseconds
|
158
|
+
subject.published = true
|
159
|
+
subject.published_at.should == time
|
160
|
+
end
|
161
|
+
|
162
|
+
describe "after publication" do
|
163
|
+
before { subject.published = true }
|
164
|
+
|
165
|
+
it "should nil out publication date" do
|
166
|
+
subject.published = false
|
167
|
+
subject.published_at.should be_blank
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should not modify publication date" do
|
171
|
+
time = subject.published_at
|
172
|
+
subject.published = true
|
173
|
+
subject.published_at.should == time
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe "with 1 code example" do
|
178
|
+
before do
|
179
|
+
FakeWeb.register_uri(:post, 'https://api.github.com/gists', :response => fixture('gist_with_1_code_example'))
|
180
|
+
end
|
181
|
+
|
182
|
+
describe "reloading the post later" do
|
183
|
+
before do
|
184
|
+
FakeWeb.register_uri(:get, 'https://api.github.com/gists/1', :response => fixture('gist_with_1_code_example'))
|
185
|
+
Mist::Post.create!(:title => 'Code Example', :content => "# Test Content\n\n file: test.rb\n def one\n 1\n end\n\n# Moar test content")
|
186
|
+
end
|
187
|
+
|
188
|
+
subject { Mist::Post.find('code-example') }
|
189
|
+
|
190
|
+
describe "changing its title" do
|
191
|
+
before { subject.title = "new title" }
|
192
|
+
|
193
|
+
it "should not change its id" do
|
194
|
+
subject.id.should == "code-example"
|
195
|
+
end
|
196
|
+
|
197
|
+
describe "and then saving the post" do
|
198
|
+
before do
|
199
|
+
subject.gist.should_receive(:save).and_return(true)
|
200
|
+
subject.save!
|
201
|
+
end
|
202
|
+
|
203
|
+
it "should update the gist's description" do
|
204
|
+
subject.gist.description.should =~ /new title/
|
205
|
+
end
|
206
|
+
|
207
|
+
it "should change its id" do
|
208
|
+
subject.id.should == "new-title"
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
describe "and then adding a new code example" do
|
214
|
+
before { subject.content << "\n file: moar-file.rb\n moar = :more\n\nDone" }
|
215
|
+
|
216
|
+
it "should find 2 code examples" do
|
217
|
+
subject.code_examples.should have(2).examples
|
218
|
+
end
|
219
|
+
|
220
|
+
it "should create a new gist file" do
|
221
|
+
subject.gist.should_receive(:save).and_return(true)
|
222
|
+
subject.save
|
223
|
+
subject.gist.files.should have_key('moar-file.rb')
|
224
|
+
subject.gist.files['moar-file.rb'].should have_key(:content)
|
225
|
+
subject.gist.files['moar-file.rb'][:content].should == "moar = :more\n"
|
226
|
+
end
|
227
|
+
|
228
|
+
describe "saving and then removing one code example" do
|
229
|
+
before do
|
230
|
+
subject.gist.should_receive(:save).twice.and_return(true)
|
231
|
+
subject.save
|
232
|
+
subject.content["\n file: moar-file.rb\n moar = :more\n"] = "\n"
|
233
|
+
subject.save
|
234
|
+
end
|
235
|
+
|
236
|
+
it "should mark moar-file.rb for deletion" do
|
237
|
+
subject.gist.files.should have_key('moar-file.rb')
|
238
|
+
subject.gist.files['moar-file.rb'].should be_nil
|
239
|
+
end
|
240
|
+
|
241
|
+
it "should not mark test.rb for deletion" do
|
242
|
+
subject.gist.files.should have_key('test.rb')
|
243
|
+
subject.gist.files['test.rb'].should_not be_nil
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
describe "and then removing the code example" do
|
249
|
+
before { subject.content = "no code examples here" }
|
250
|
+
|
251
|
+
it "should find 0 code examples" do
|
252
|
+
subject.code_examples.should be_empty
|
253
|
+
end
|
254
|
+
|
255
|
+
it "should delete the gist" do
|
256
|
+
subject.gist.should_receive(:destroy).and_return(true)
|
257
|
+
subject.save
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
describe "with the gist now missing" do
|
262
|
+
before do
|
263
|
+
FakeWeb.register_uri(:get, 'https://api.github.com/gists/1', :response => fixture('gist_404'))
|
264
|
+
end
|
265
|
+
|
266
|
+
it "should not raise an error" do
|
267
|
+
proc { subject }.should_not raise_error
|
268
|
+
end
|
269
|
+
|
270
|
+
it "should embed code using regular markdown" do
|
271
|
+
subject.content_as_html.should_not =~ /gist.github.com/
|
272
|
+
end
|
273
|
+
|
274
|
+
describe "when saving the record" do
|
275
|
+
it "should create a new gist" do
|
276
|
+
subject.save!
|
277
|
+
subject.gist.should be_persisted
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
it "should ensure a blank line before and after gist embeds" do
|
283
|
+
# otherwise not having the blanks will cause markdown to not handle headers properly
|
284
|
+
content = subject.content_with_embedded_gists
|
285
|
+
content.should =~ /\n\n<script.*?<\/script>\n\n/
|
286
|
+
end
|
287
|
+
|
288
|
+
it "should still have the gist" do
|
289
|
+
subject.gist.should be_persisted
|
290
|
+
end
|
291
|
+
|
292
|
+
it "should contain gist code" do
|
293
|
+
# so the blogger can later edit the gist.
|
294
|
+
# Also, if the gist is externally upated, it should be reflected
|
295
|
+
# when blog post is updated. This test shows as much because in
|
296
|
+
# the fakeweb response, 1 is switched with :one.
|
297
|
+
subject.content.should == "# Test Content\n\n file: test.rb\n def one\n :one\n end\n\n# Moar test content"
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
describe "constructing a new post" do
|
302
|
+
before do
|
303
|
+
subject.title = "Code Example"
|
304
|
+
subject.content = "# Test Content\n\n file: test.rb\n def one\n 1\n end\n\n# Moar test content"
|
305
|
+
end
|
306
|
+
|
307
|
+
describe "after saving" do
|
308
|
+
before do
|
309
|
+
subject.gist.should_receive(:save).and_return(true)
|
310
|
+
subject.save
|
311
|
+
end
|
312
|
+
|
313
|
+
it "should know its own url" do
|
314
|
+
subject.url.should == "http://example.com/posts/code-example"
|
315
|
+
end
|
316
|
+
|
317
|
+
describe "the gist description" do
|
318
|
+
let(:desc) { subject.gist.description }
|
319
|
+
|
320
|
+
it "include link to post" do
|
321
|
+
desc.should =~ /example.com\/posts\/code-example/
|
322
|
+
end
|
323
|
+
|
324
|
+
it "should not include code example filename" do
|
325
|
+
subject.gist.description.should_not =~ /test.rb/
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
end
|
330
|
+
it { should have_code_examples }
|
331
|
+
|
332
|
+
it "should embed the gist in html" do
|
333
|
+
subject.save!
|
334
|
+
embed = '<script src="https://gist.github.com/1.js?file=test.rb"></script>'
|
335
|
+
subject.content_as_html.should =~ /#{Regexp::escape embed}/
|
336
|
+
end
|
337
|
+
|
338
|
+
it "should not embed the gist in html preview" do
|
339
|
+
subject.save!
|
340
|
+
subject.content_as_html_preview.should_not =~ /gist.github.com/
|
341
|
+
end
|
342
|
+
|
343
|
+
it "should not embed code in html preview" do
|
344
|
+
subject.save!
|
345
|
+
subject.content_as_html_preview.should_not =~ /file: test.rb/
|
346
|
+
subject.content_as_html_preview.should_not =~ /def one/
|
347
|
+
end
|
348
|
+
|
349
|
+
it "should include the first line in html preview" do
|
350
|
+
subject.save!
|
351
|
+
subject.content_as_html_preview.should =~ /Test Content/
|
352
|
+
end
|
353
|
+
|
354
|
+
it "should not include moar content in the html preview" do
|
355
|
+
subject.save!
|
356
|
+
subject.content_as_html_preview.should_not =~ /Moar test content/
|
357
|
+
end
|
358
|
+
|
359
|
+
it "should not embed gist info if there are no code examples" do
|
360
|
+
subject.content = "No code"
|
361
|
+
subject.save!
|
362
|
+
subject.content_as_html.should_not =~ /gist.github.com/
|
363
|
+
end
|
364
|
+
|
365
|
+
it "should use the example's filename" do
|
366
|
+
# we don't want the fake response to modify the gist this time
|
367
|
+
subject.gist.stub(:save).and_return(true)
|
368
|
+
subject.save!
|
369
|
+
subject.gist.files.should have_key('test.rb')
|
370
|
+
end
|
371
|
+
|
372
|
+
it "should identify 1 code example" do
|
373
|
+
subject.code_examples.length.should == 1
|
374
|
+
subject.code_examples.first.should == "def one\n 1\nend\n"
|
375
|
+
end
|
376
|
+
|
377
|
+
it "should not have saved a gist yet" do
|
378
|
+
subject.gist.should_not be_persisted
|
379
|
+
end
|
380
|
+
|
381
|
+
describe "saving" do
|
382
|
+
it "should save a gist" do
|
383
|
+
subject.save!
|
384
|
+
subject.gist.should be_persisted
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
describe "with no code examples" do
|
391
|
+
before do
|
392
|
+
subject.title = "No Code Example"
|
393
|
+
subject.content = "# Test Content"
|
394
|
+
end
|
395
|
+
|
396
|
+
it { should_not have_code_examples }
|
397
|
+
|
398
|
+
it "should not have a gist at all" do
|
399
|
+
subject.gist.should be_nil
|
400
|
+
end
|
401
|
+
|
402
|
+
it "should not create a gist" do
|
403
|
+
subject.save!
|
404
|
+
subject.gist.should be_nil
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
describe "validation" do
|
409
|
+
before { subject.valid? }
|
410
|
+
|
411
|
+
it "should require title" do
|
412
|
+
subject.errors[:title].should include("can't be blank")
|
413
|
+
end
|
414
|
+
|
415
|
+
it "should require content" do
|
416
|
+
subject.errors[:content].should include("can't be blank")
|
417
|
+
end
|
418
|
+
|
419
|
+
it "should enforce uniqueness of title" do
|
420
|
+
create :post
|
421
|
+
post = build :post
|
422
|
+
post.valid?
|
423
|
+
post.errors[:title].should include("has already been taken")
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
describe "creation" do
|
428
|
+
it "should return the post" do
|
429
|
+
Mist::Post.create!(attributes_for :post).should be_kind_of(Mist::Post)
|
430
|
+
end
|
431
|
+
|
432
|
+
it "should not be a new record" do
|
433
|
+
Mist::Post.create!(attributes_for :post).should_not be_new_record
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
describe "new valid record" do
|
438
|
+
subject { build :post }
|
439
|
+
|
440
|
+
it { should_not be_persisted }
|
441
|
+
it { should be_new_record }
|
442
|
+
it { should be_changed }
|
443
|
+
|
444
|
+
describe "after saving" do
|
445
|
+
before { subject.save! }
|
446
|
+
|
447
|
+
it { should be_persisted }
|
448
|
+
it { should_not be_new_record }
|
449
|
+
it { should_not be_changed }
|
450
|
+
|
451
|
+
it "should increase count" do
|
452
|
+
Mist::Post.count.should == 1
|
453
|
+
end
|
454
|
+
|
455
|
+
it "should have created an attribute file" do
|
456
|
+
File.should be_file(subject.path)
|
457
|
+
end
|
458
|
+
|
459
|
+
it "should have created a commit" do
|
460
|
+
Mist.log.size.should == 1
|
461
|
+
end
|
462
|
+
|
463
|
+
it "should load the content in a separate query" do
|
464
|
+
Mist::Post.find(subject.id).content.should == subject.content
|
465
|
+
end
|
466
|
+
end
|
467
|
+
end
|
468
|
+
|
469
|
+
describe "an existing record" do
|
470
|
+
subject { create :post }
|
471
|
+
|
472
|
+
describe "destroying" do
|
473
|
+
before { subject.destroy }
|
474
|
+
|
475
|
+
it "should not be found" do
|
476
|
+
Mist::Post.find(subject.id).should be_nil
|
477
|
+
end
|
478
|
+
|
479
|
+
it "should produce a commit" do
|
480
|
+
Mist.log.size.should == 2
|
481
|
+
end
|
482
|
+
|
483
|
+
it "should reduce count" do
|
484
|
+
Mist::Post.count.should == 0
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
488
|
+
describe "changing its content" do
|
489
|
+
before { subject.content = "changed"; subject.save! }
|
490
|
+
|
491
|
+
it "should create a commit" do
|
492
|
+
Mist.log.size.should == 2
|
493
|
+
end
|
494
|
+
|
495
|
+
it "should load the new content in a separate query" do
|
496
|
+
Mist::Post.find(subject.id).content.should == "changed"
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
describe "changing its subject" do
|
501
|
+
before do
|
502
|
+
@old_path = subject.path
|
503
|
+
subject.title = "changed"
|
504
|
+
subject.save!
|
505
|
+
end
|
506
|
+
|
507
|
+
it "should create a commit" do
|
508
|
+
Mist.log.size.should == 2
|
509
|
+
end
|
510
|
+
|
511
|
+
it "should have changed the content path" do
|
512
|
+
@old_path.should_not == subject.path
|
513
|
+
end
|
514
|
+
|
515
|
+
it "should have removed the old content path" do
|
516
|
+
File.should_not exist(@old_path)
|
517
|
+
end
|
518
|
+
|
519
|
+
it "should hvae created a new content path" do
|
520
|
+
File.should be_file(subject.path)
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
describe "changing its subject and its content simultaneously" do
|
525
|
+
before do
|
526
|
+
@old_path = subject.path
|
527
|
+
subject.title = "changed"
|
528
|
+
subject.content = "changed"
|
529
|
+
subject.save!
|
530
|
+
end
|
531
|
+
|
532
|
+
it "should create a commit" do
|
533
|
+
Mist.log.size.should == 2
|
534
|
+
end
|
535
|
+
|
536
|
+
it "should have changed the content path" do
|
537
|
+
@old_path.should_not == subject.path
|
538
|
+
end
|
539
|
+
|
540
|
+
it "should have removed the old content path" do
|
541
|
+
File.should_not exist(@old_path)
|
542
|
+
end
|
543
|
+
|
544
|
+
it "should have created a new content path" do
|
545
|
+
File.should be_file(subject.path)
|
546
|
+
end
|
547
|
+
|
548
|
+
it "should load the new content in a separate query" do
|
549
|
+
Mist::Post.find(subject.id).content.should == "changed"
|
550
|
+
end
|
551
|
+
end
|
552
|
+
end
|
553
|
+
|
554
|
+
describe "active model lint tests" do
|
555
|
+
# make sure we didn't break them
|
556
|
+
|
557
|
+
include Test::Unit::Assertions
|
558
|
+
include ActiveModel::Lint::Tests
|
559
|
+
|
560
|
+
def model
|
561
|
+
@model ||= Class.new(Mist::GitModel) do
|
562
|
+
def self.name
|
563
|
+
"TestModel"
|
564
|
+
end
|
565
|
+
end.new
|
566
|
+
end
|
567
|
+
|
568
|
+
# to_s is to support ruby-1.9
|
569
|
+
ActiveModel::Lint::Tests.public_instance_methods.map{|m| m.to_s}.grep(/^test/).each do |m|
|
570
|
+
example m.gsub('_',' ') do
|
571
|
+
send m
|
572
|
+
end
|
573
|
+
end
|
574
|
+
end
|
575
|
+
end
|