publishable 0.3.0 → 1.0.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.
@@ -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