publishable 0.3.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,8 @@
1
1
  require 'rails/railtie'
2
2
 
3
3
  module Publishable
4
+
5
+ # Extend ActiveRecord::Base to enable the +publishable+ DSL.
4
6
  class Railtie < Rails::Railtie
5
7
  initializer 'publishable.initialize' do |app|
6
8
  ActiveRecord::Base.extend Publishable::ClassMethods
@@ -0,0 +1,40 @@
1
+ # encoding: utf-8
2
+
3
+ module Publishable
4
+
5
+ # Defines the current version for this gem. Versions are specified as a dot-delimited string:
6
+ #
7
+ # major.minor.patch.prerelease+build
8
+ #
9
+ # When incrementing any field, all lower-rank fields should be reset to zero or nil.
10
+ #
11
+ # @author David Daniell / тιηуηυмвєяѕ <info@tinynumbers.com>
12
+ module VERSION
13
+
14
+ # The major version number, only incremented for a major overhaul.
15
+ MAJOR = 1
16
+
17
+ # The minor version number, incremented for significant releases of new features.
18
+ MINOR = 0
19
+
20
+ # The patch-level, incremented for minor bug fixes / patches.
21
+ PATCH = 0
22
+
23
+ # Prerelease specification for e.g. "alpha", "beta.1", etc
24
+ PRERELEASE = nil
25
+
26
+ # The build number; can be used for e.g. git version of current build, etc.
27
+ BUILD = nil
28
+
29
+ # Return the version as a dot-delimited string.
30
+ # @return [String] the current gem version
31
+ def self.to_s
32
+ @version_string ||= begin
33
+ v = "#{MAJOR}.#{MINOR}.#{PATCH}"
34
+ v = PRERELEASE ? "#{v}.#{PRERELEASE}" : v
35
+ BUILD ? "#{v}+#{BUILD}" : v
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -1,57 +1,65 @@
1
1
  # Generated by jeweler
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
- s.name = %q{publishable}
8
- s.version = "0.3.0"
7
+ s.name = "publishable"
8
+ s.version = "1.0.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Martin Linkhorst"]
12
- s.date = %q{2010-09-29}
13
- s.description = %q{Provides methods to publish and unpublish your active record models based on a datetime. Also adds named scopes to nicely filter your records. Does not touch any controller or views.}
14
- s.email = %q{m.linkhorst@googlemail.com}
11
+ s.authors = ["Martin Linkhorst", "\321\202\316\271\316\267\321\203\316\267\317\205\320\274\320\262\321\224\321\217\321\225"]
12
+ s.date = "2013-03-07"
13
+ s.description = "Provides methods to publish and unpublish your active record models based on a boolean flag, a date, or a datetime. Also adds named scopes to nicely filter your records. Does not touch any controller or views."
14
+ s.email = ["m.linkhorst@googlemail.com", "info@tinynumbers.com"]
15
15
  s.extra_rdoc_files = [
16
- "LICENSE",
17
- "README.textile"
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
18
  ]
19
19
  s.files = [
20
20
  ".document",
21
- ".gitignore",
22
- "LICENSE",
23
- "README.textile",
24
- "Rakefile",
25
- "VERSION",
26
- "db/schema.rb",
27
- "lib/publishable.rb",
28
- "lib/publishable/railtie.rb",
29
- "publishable.gemspec",
30
- "spec/publishable_spec.rb",
31
- "spec/spec.opts",
32
- "spec/spec_helper.rb"
33
- ]
34
- s.homepage = %q{http://github.com/linki/publishable}
35
- s.rdoc_options = ["--charset=UTF-8"]
36
- s.require_paths = ["lib"]
37
- s.rubygems_version = %q{1.3.7}
38
- s.summary = %q{Adds publishing functionality to your active record model}
39
- s.test_files = [
21
+ ".rspec",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "LICENSE.txt",
25
+ "README.rdoc",
26
+ "Rakefile",
27
+ "lib/publishable.rb",
28
+ "lib/publishable/railtie.rb",
29
+ "lib/publishable/version.rb",
30
+ "publishable.gemspec",
40
31
  "spec/publishable_spec.rb",
41
- "spec/spec_helper.rb"
32
+ "spec/spec_helper.rb",
33
+ "spec/support/matcher_each.rb"
42
34
  ]
35
+ s.homepage = "http://github.com/linki/publishable"
36
+ s.licenses = ["MIT"]
37
+ s.require_paths = ["lib"]
38
+ s.rubygems_version = "1.8.24"
39
+ s.summary = "Adds publishing functionality to your active record model"
43
40
 
44
41
  if s.respond_to? :specification_version then
45
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
46
42
  s.specification_version = 3
47
43
 
48
44
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
49
- s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
45
+ s.add_development_dependency(%q<yard>, ["~> 0.7"])
46
+ s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
47
+ s.add_development_dependency(%q<bundler>, ["~> 1.0"])
48
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
49
+ s.add_development_dependency(%q<rspec>, ["~> 2.8.0"])
50
50
  else
51
- s.add_dependency(%q<rspec>, [">= 1.2.9"])
51
+ s.add_dependency(%q<yard>, ["~> 0.7"])
52
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
53
+ s.add_dependency(%q<bundler>, ["~> 1.0"])
54
+ s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
55
+ s.add_dependency(%q<rspec>, ["~> 2.8.0"])
52
56
  end
53
57
  else
54
- s.add_dependency(%q<rspec>, [">= 1.2.9"])
58
+ s.add_dependency(%q<yard>, ["~> 0.7"])
59
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
60
+ s.add_dependency(%q<bundler>, ["~> 1.0"])
61
+ s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
62
+ s.add_dependency(%q<rspec>, ["~> 2.8.0"])
55
63
  end
56
64
  end
57
65
 
@@ -1,98 +1,296 @@
1
- require File.expand_path('../spec_helper', __FILE__)
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
- class Album < ActiveRecord::Base
4
- extend Publishable
5
- publishable
6
- end
3
+ describe Publishable do
7
4
 
8
- class Feature < ActiveRecord::Base
9
- extend Publishable
10
- publishable :on => :public_since
11
- end
5
+ context 'with a Boolean publish attribute' do
12
6
 
13
- describe Publishable do
14
- ActiveRecord::Base.establish_connection :adapter => 'sqlite3',
15
- :database => File.expand_path('../../db/test.sqlite3', __FILE__)
16
- load('db/schema.rb')
17
-
18
- before do
19
- Album.delete_all
20
- @album = Album.new
21
- end
22
-
23
- it "should not be published" do
24
- # nil or false
25
- @album.published_at.should be_false
26
- @album.should_not be_published
27
-
28
- # in the future
29
- @album.published_at = Time.now + 60
30
- @album.should_not be_published
31
- end
7
+ before :all do
8
+ build_model :post do
9
+ string :title
10
+ text :body
11
+ boolean :published
12
+ attr_accessible :title, :body, :published
13
+ validates :body, :title, :presence => true
14
+ extend Publishable
15
+ publishable
16
+ end
17
+ end
18
+
19
+ before :each do
20
+ @post = Post.new :title => Faker::Lorem.sentence(4), :body => Faker::Lorem.paragraphs(3).join("\n")
21
+ @post.should be_valid
22
+ @post.should_not be_published
23
+ end
24
+
25
+ it 'should become published when publish flag is set' do
26
+ @post.publish!
27
+ @post.should be_published
28
+ end
29
+
30
+ it 'should become unpublished when publish flag is cleared' do
31
+ @post.published = true
32
+ @post.should be_published
33
+ @post.unpublish!
34
+ @post.should_not be_published
35
+ end
32
36
 
33
- it "should be published" do
34
- # now
35
- @album.published_at = Time.now
36
- @album.should be_published
37
-
38
- # in the past
39
- @album.published_at = Time.now - 60
40
- @album.should be_published
41
- end
42
-
43
- it "should be publishable" do
44
- @album.should_not be_published
45
- @album.publish
46
- @album.should be_published
47
- end
48
-
49
- it "should persist" do
50
- @album.expects(:publish).returns(true)
51
- @album.expects(:save)
52
- @album.publish!
53
37
  end
54
-
55
- describe "scope" do
56
- before do
57
- @album_1 = Album.create!(:published_at => nil)
58
- @album_2 = Album.create!(:published_at => Time.now - 60)
59
- @album_3 = Album.create!(:published_at => Time.now)
60
- @album_4 = Album.create!(:published_at => Time.now + 60)
61
- end
62
-
63
- it "should find published records" do
64
- Album.published.should == [@album_2, @album_3]
65
- end
66
-
67
- it "should find unpublished records" do
68
- Album.unpublished.should == [@album_1, @album_4]
38
+
39
+ context 'with a Date publish attribute' do
40
+
41
+ before :all do
42
+ build_model :post do
43
+ string :title
44
+ text :body
45
+ date :published
46
+ attr_accessible :title, :body, :published
47
+ validates :body, :title, :presence => true
48
+ extend Publishable
49
+ publishable
50
+ end
51
+ end
52
+
53
+ before :each do
54
+ @post = Post.new :title => Faker::Lorem.sentence(4),
55
+ :body => Faker::Lorem.paragraphs(3).join("\n")
56
+ @post.should be_valid
57
+ @post.should_not be_published
58
+ end
59
+
60
+ it 'should be published if today is after the publish date' do
61
+ @post.published = Date.current - 1.days
62
+ @post.should be_published
69
63
  end
64
+
65
+ it 'should be published if today is the publish date' do
66
+ @post.published = Date.current
67
+ @post.should be_published
68
+ end
69
+
70
+ it 'should not be published if today is before the publish date' do
71
+ @post.published = Date.current + 1.days
72
+ @post.should_not be_published
73
+ end
74
+
75
+ it 'should have a publish date of today or earlier after publish is directly called' do
76
+ @post.publish
77
+ @post.should be_published
78
+ @post.published.should <= Date.current
79
+ end
80
+
70
81
  end
71
-
72
- describe "other column" do
73
- it "should be publishable" do
74
- @feature = Feature.new
75
- @feature.should_not be_published
76
- @feature.publish
77
- @feature.should be_published
82
+
83
+ context 'with a DateTime publish attribute' do
84
+
85
+ before :all do
86
+ build_model :post do
87
+ string :title
88
+ text :body
89
+ datetime :published
90
+ attr_accessible :title, :body, :published
91
+ validates :body, :title, :presence => true
92
+ extend Publishable
93
+ publishable
94
+ end
95
+ end
96
+
97
+ describe 'publishing and unpublishing' do
98
+
99
+ before :each do
100
+ @post = Post.new :title => Faker::Lorem.sentence(4),
101
+ :body => Faker::Lorem.paragraphs(3).join("\n")
102
+ @post.should be_valid
103
+ @post.should_not be_published
104
+ end
105
+
106
+ it 'should be published if now is after the publish time' do
107
+ @post.published = DateTime.now - 1.minute
108
+ @post.should be_published
109
+ end
110
+
111
+ it 'should not be published if now is before the publish time' do
112
+ @post.published = DateTime.now + 1.minute
113
+ @post.should_not be_published
114
+ end
115
+
116
+ it 'should have a publish time of now or earlier after publish is directly called' do
117
+ @post.publish
118
+ @post.should be_published
119
+ @post.published.should <= DateTime.now
120
+ end
121
+
122
+ end
123
+
124
+ describe 'querying for all upcoming items' do
125
+
126
+ before :all do
127
+ # create a bunch of published Posts
128
+ (rand(9) + 1).times do
129
+ days_ago = (rand(100) + 1).days
130
+ Post.create :published => Date.current - days_ago,
131
+ :title => Faker::Lorem.sentence(4),
132
+ :body => Faker::Lorem.paragraphs(3).join("\n")
133
+ end
134
+ end
135
+
136
+ after :each do
137
+ Post.upcoming.destroy_all
138
+ end
139
+
140
+ after :all do
141
+ # have to destroy posts between tests - these are fake, not in the DB, so database-cleaner won't help us
142
+ Post.destroy_all
143
+ end
144
+
145
+ [1, 2, 5, 10].each do |how_many|
146
+ it "should return all #{how_many} upcoming queries if no limit is specified" do
147
+ # create a known number of unpublished Posts
148
+ how_many.times do
149
+ days_from_now = (rand(100) + 1).days
150
+ Post.create :published => Date.current + days_from_now,
151
+ :title => Faker::Lorem.sentence(4),
152
+ :body => Faker::Lorem.paragraphs(3).join("\n")
153
+ end
154
+ size = Post.upcoming.size
155
+ size.should == how_many
156
+ end
157
+ end
158
+
78
159
  end
79
-
80
- describe "scope" do
81
- before do
82
- Feature.delete_all
83
- @feature_1 = Feature.create!(:public_since => nil)
84
- @feature_2 = Feature.create!(:public_since => Time.now - 60)
85
- @feature_3 = Feature.create!(:public_since => Time.now)
86
- @feature_4 = Feature.create!(:public_since => Time.now + 60)
160
+
161
+ describe 'querying for all recent items' do
162
+
163
+ before :all do
164
+ # create a bunch of unpublished posts
165
+ (rand(7) + 3).times do
166
+ days_from_now = (rand(100) + 1).days
167
+ Post.create :published => Date.current + days_from_now,
168
+ :title => Faker::Lorem.sentence(4),
169
+ :body => Faker::Lorem.paragraphs(3).join("\n")
170
+ end
171
+ end
172
+
173
+ after :each do
174
+ Post.recent.destroy_all
175
+ end
176
+
177
+ after :all do
178
+ Post.destroy_all
179
+ end
180
+
181
+ [1, 2, 5, 10].each do |how_many|
182
+ it "should return all #{how_many} recent queries if no limit is specified" do
183
+ # create a known number of published posts
184
+ how_many.times do
185
+ Post.create :published => Date.current - rand(100).days,
186
+ :title => Faker::Lorem.sentence(4),
187
+ :body => Faker::Lorem.paragraphs(3).join("\n")
188
+ end
189
+ Post.recent.size.should == how_many
190
+ end
191
+ end
192
+
193
+ end
194
+
195
+ describe 'queries for recent or upcoming items' do
196
+
197
+ before :all do
198
+ # this is more a test for Publishable than it is for Story, but let's set the time to near the end of the day
199
+ # this was causing a failing condition due to UTC/local TZ differences
200
+ new_time = Time.local(2013, 01, 23, 22, 0, 0)
201
+ Timecop.travel(new_time)
202
+
203
+ (-5..5).each do |n|
204
+ Post.create :published => Date.current + n.days,
205
+ :title => Faker::Lorem.sentence(4),
206
+ :body => Faker::Lorem.paragraphs(3).join("\n")
207
+ end
87
208
  end
88
209
 
89
- it "should find published records" do
90
- Feature.published.should == [@feature_2, @feature_3]
210
+ after :all do
211
+ # go back to the normal time and date
212
+ Timecop.return
213
+ Post.destroy_all
91
214
  end
92
215
 
93
- it "should find unpublished records" do
94
- Feature.unpublished.should == [@feature_1, @feature_4]
216
+ context 'recent' do
217
+
218
+ it 'returns only published items' do
219
+ Post.recent.should each be_published
220
+ end
221
+
222
+ it 'returns the requested number of items' do
223
+ Post.recent(2).size.should == 2
224
+ end
225
+
226
+ it 'returns as many items as available if we request too many' do
227
+ Post.recent(10).size.should == 6
228
+ end
229
+
230
+ end
231
+
232
+ context 'upcoming' do
233
+
234
+ it 'returns only unpublished items' do
235
+ Post.upcoming.should each be_unpublished
236
+ end
237
+
238
+ it 'returns the requested number of items' do
239
+ Post.upcoming(2).size.should == 2
240
+ end
241
+
242
+ it 'returns as many items as available if we request too many' do
243
+ Post.upcoming(50).size.should == 5
244
+ end
245
+
95
246
  end
96
- end
247
+
248
+ end
249
+
97
250
  end
251
+
252
+ describe 'with an invalid configuration' do
253
+
254
+ it 'should raise a configuration error when defined on an invalid column type' do
255
+ expect {
256
+ build_model :post do
257
+ string :title
258
+ text :body
259
+ attr_accessible :title, :body
260
+ validates :body, :title, :presence => true
261
+ extend Publishable
262
+ publishable :on => :title
263
+ end
264
+ }.to raise_error ActiveRecord::ConfigurationError
265
+ end
266
+
267
+ it 'should raise a configuration error when the publish column not defined' do
268
+ expect {
269
+ build_model :post do
270
+ string :title
271
+ text :body
272
+ attr_accessible :title, :body
273
+ validates :body, :title, :presence => true
274
+ extend Publishable
275
+ publishable
276
+ end
277
+ }.to raise_error ActiveRecord::ConfigurationError
278
+ end
279
+
280
+ it 'should raise a configuration error when defined on a missing column' do
281
+ expect {
282
+ build_model :post do
283
+ string :title
284
+ text :body
285
+ datetime :published
286
+ attr_accessible :title, :body, :published
287
+ validates :body, :title, :presence => true
288
+ extend Publishable
289
+ publishable :on => :foobar
290
+ end
291
+ }.to raise_error ActiveRecord::ConfigurationError
292
+ end
293
+
294
+ end
295
+
98
296
  end