has_publishing 0.0.1
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 +18 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +67 -0
- data/Rakefile +6 -0
- data/has_publishing.gemspec +24 -0
- data/lib/has_publishing/class_methods.rb +18 -0
- data/lib/has_publishing/configuration.rb +7 -0
- data/lib/has_publishing/generators/publishing_generator.rb +28 -0
- data/lib/has_publishing/generators/templates/migration.rb.erb +13 -0
- data/lib/has_publishing/instance_methods.rb +72 -0
- data/lib/has_publishing/version.rb +3 -0
- data/lib/has_publishing.rb +30 -0
- data/spec/has_publishing_spec.rb +328 -0
- metadata +131 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 3months
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# HasPublishing
|
2
|
+
|
3
|
+
Mark models as `has_publishing` to publish, draft and embargo models. Easy peasy!
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
* `published`, `draft`, `embargoed` scopes for easy filtering/finding
|
8
|
+
* Rails environment-based default scoping: if your site is using `production`, draft/embargoed records will still be found - if you use `RAILS_ENV=production_published`, though, only published records will be found.
|
9
|
+
* In use in production on multiple sites
|
10
|
+
* Covered by automated tests
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Add this line to your application's Gemfile:
|
15
|
+
|
16
|
+
gem 'has_publishing'
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
$ bundle
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
|
24
|
+
$ gem install has_publishing
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
Simply add `has_publishing` to the model of your choice, and then generate the publishing attributes for your model:
|
29
|
+
|
30
|
+
``` bash
|
31
|
+
bundle exec rails generate migration [YOUR MODEL NAME] embargoed_until:datetime published_at:datetime published_id:integer kind:string
|
32
|
+
```
|
33
|
+
|
34
|
+
…and then of course run `rake db:migrate`
|
35
|
+
|
36
|
+
(If anyone would like to add a generator to automate this process, it would be very much appreciated)
|
37
|
+
|
38
|
+
## A note on publishing
|
39
|
+
|
40
|
+
Publishing is typically used in an environment where there may be two installations of the Rails application sharing a common database. This at least is the set up that `has_publishing` is designed to operate in - something like the following:
|
41
|
+
|
42
|
+
|
43
|
+
```
|
44
|
+
|-- Admin RAILS_ENV=production --| >>>>> SharedDatabase <<<<<< |-- Published Site RAILS_ENV=production_published --|
|
45
|
+
```
|
46
|
+
|
47
|
+
Because of this, the gem applies a default_scope to all instances of this model to either:
|
48
|
+
|
49
|
+
* Only return published records if `Rails.env` matches `HasPublishing.config.published_rails_environment`
|
50
|
+
* Only return draft records otherwise
|
51
|
+
|
52
|
+
This prevents 'duplicate' records from appearing for the user (since each 'record' has two representations - 'draft' and 'published/withdrawn')
|
53
|
+
|
54
|
+
### So:
|
55
|
+
|
56
|
+
1. If you would prefer that this default scope **NOT** be applied, then simply set `HasPublishing.config.scope_records` to `false`.
|
57
|
+
2. If you want the default scope to apply properly, ensure that you set the Rails environment of your **published** application in `HasPublishing.config.published_rails_environment`.
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
## Contributing
|
62
|
+
|
63
|
+
1. Fork it
|
64
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
65
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
66
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
67
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'has_publishing/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "has_publishing"
|
8
|
+
gem.version = HasPublishing::VERSION
|
9
|
+
gem.authors = ["Josh McArthur @ 3months"]
|
10
|
+
gem.email = ["joshua.mcarthur@gmail.com"]
|
11
|
+
gem.description = %q{Add ability to draft/publish/withdraw/embargo models}
|
12
|
+
gem.summary = %q{Add publishing to your ActiveRecord models}
|
13
|
+
gem.homepage = "https://github.com/3months/has_publishing"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency "activerecord"
|
21
|
+
gem.add_dependency "activesupport"
|
22
|
+
gem.add_development_dependency "rspec-rails"
|
23
|
+
gem.add_development_dependency "sqlite3"
|
24
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module HasPublishing
|
2
|
+
module ClassMethods
|
3
|
+
# This default scope allows published and draft viewing modes to share the
|
4
|
+
# same code. This is good. However if you need to access the other, draft
|
5
|
+
# from published or published from draft e.g from admin for editing, then
|
6
|
+
# you must explicitly use .unscoped to remove the default scope.
|
7
|
+
|
8
|
+
def default_scope
|
9
|
+
return scoped if HasPublishing.config.scope_records == false
|
10
|
+
|
11
|
+
if Rails.env == (HasPublishing.config.published_rails_environment)
|
12
|
+
published
|
13
|
+
else
|
14
|
+
draft
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
|
4
|
+
class HasPublishing::Generators::PublishingGenerator < ::Rails::Generators::Base
|
5
|
+
include Rails::Generators::Migration
|
6
|
+
|
7
|
+
def self.source_root
|
8
|
+
@source_root ||= File.join(File.dirname(__FILE__), 'templates')
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.next_migration_number(dirname)
|
12
|
+
if ActiveRecord::Base.timestamped_migrations
|
13
|
+
Time.new.utc.strftime("%Y%m%d%H%M%S")
|
14
|
+
else
|
15
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_migration_file
|
20
|
+
set_local_assigns!
|
21
|
+
validate_file_name!
|
22
|
+
migration_template 'migration.rb', "db/migrate/publishing_fields_for_#{@table_name}.rb"
|
23
|
+
end
|
24
|
+
|
25
|
+
def set_local_assigns!
|
26
|
+
@table_name = file_name.pluralize
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class PublishingFieldsFor<%= @table_name %> < ActiveRecord::Migration
|
2
|
+
def self.change
|
3
|
+
change_table <%= @table_name %> do |t|
|
4
|
+
t.datetime :published_at
|
5
|
+
t.datetime :embargoed_until
|
6
|
+
t.string :kind, :null => false, :index => true
|
7
|
+
t.boolean :dirty
|
8
|
+
|
9
|
+
# These should already be present
|
10
|
+
t.timestamps
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module HasPublishing
|
2
|
+
module InstanceMethods
|
3
|
+
|
4
|
+
|
5
|
+
# Publishing "states"
|
6
|
+
# Draft = draft only (no published version, i.e new)
|
7
|
+
# Dirty = published + draft dirty (this is a kind of Draft state,
|
8
|
+
# has been published but draft has been edited since)
|
9
|
+
# Published = published + draft NOT dirty
|
10
|
+
# Under imbargo = published but future dated (embargo_until field)
|
11
|
+
|
12
|
+
|
13
|
+
def publish!(extra_attrs = {})
|
14
|
+
self.save! if self.new_record?
|
15
|
+
self.class.unscoped {
|
16
|
+
return false unless draft?
|
17
|
+
if published.nil? # first time publishing
|
18
|
+
published_obj = self.class.create!(attributes.merge(:kind => 'published', :published_at => Time.zone.now, :dirty => nil).merge(extra_attrs))
|
19
|
+
self.published_id = published_obj.id
|
20
|
+
else
|
21
|
+
published.update_attributes!(attributes.merge(:kind => 'published', :published_id => nil, :published_at => Time.zone.now, :dirty => nil).merge(extra_attrs))
|
22
|
+
end
|
23
|
+
self.class.record_timestamps = false # want same updated_at
|
24
|
+
self.save! # make sure this model is in sync
|
25
|
+
update_attributes!(:published_at => published.published_at, :dirty => false, :updated_at => published.updated_at)
|
26
|
+
self.class.record_timestamps = true
|
27
|
+
return published
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def withdraw!
|
32
|
+
self.class.unscoped {
|
33
|
+
return false unless draft? && ever_published?
|
34
|
+
self.class.record_timestamps = false # want same updated_at
|
35
|
+
published.update_attributes!(:kind => 'withdrawn') and update_attributes!(published.attributes.merge({:kind => 'draft', :published_id => published_id, :published_at => nil, :dirty => false}))
|
36
|
+
self.class.record_timestamps = true
|
37
|
+
return published
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def draft?
|
42
|
+
kind == 'draft'
|
43
|
+
end
|
44
|
+
|
45
|
+
def published?
|
46
|
+
self.class.unscoped { (draft? && published && published.kind == 'published') || kind == 'published' }
|
47
|
+
end
|
48
|
+
|
49
|
+
def withdrawn?
|
50
|
+
self.class.unscoped { (draft? && published && published.kind == 'withdrawn') || kind == 'withdrawn' }
|
51
|
+
end
|
52
|
+
|
53
|
+
def ever_published?
|
54
|
+
published? || withdrawn?
|
55
|
+
end
|
56
|
+
|
57
|
+
def under_embargo?
|
58
|
+
published? && embargoed_until && embargoed_until > Time.zone.now
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def set_dirty
|
64
|
+
self.class.unscoped { update_attributes(:dirty => true) if draft? && published && published.updated_at != updated_at && !dirty? && !withdrawn? }
|
65
|
+
end
|
66
|
+
|
67
|
+
def set_draft
|
68
|
+
self.kind = 'draft' if kind.nil?
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'has_publishing/version'
|
2
|
+
require 'has_publishing/class_methods'
|
3
|
+
require 'has_publishing/instance_methods'
|
4
|
+
require 'has_publishing/configuration'
|
5
|
+
|
6
|
+
|
7
|
+
class << ActiveRecord::Base
|
8
|
+
def has_publishing
|
9
|
+
|
10
|
+
# Include instance methods
|
11
|
+
include HasPublishing::InstanceMethods
|
12
|
+
|
13
|
+
# Include dynamic class methods
|
14
|
+
extend HasPublishing::ClassMethods
|
15
|
+
|
16
|
+
|
17
|
+
scope :published, lambda { where(:kind => "published").not_embargoed }
|
18
|
+
scope :draft, where(:kind => 'draft')
|
19
|
+
|
20
|
+
scope :embargoed, lambda { where("embargoed_until IS NOT NULL AND ? > embargoed_until", Time.zone.now.round) }
|
21
|
+
scope :not_embargoed, lambda { where("embargoed_until IS NULL OR embargoed_until < ?", Time.zone.now.round) }
|
22
|
+
|
23
|
+
before_create :set_draft
|
24
|
+
after_save :set_dirty
|
25
|
+
|
26
|
+
belongs_to :published, :class_name => self.name, :foreign_key => :published_id, :dependent => :destroy
|
27
|
+
has_one :draft, :class_name => self.name, :foreign_key => :published_id
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,328 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'has_publishing'
|
4
|
+
|
5
|
+
|
6
|
+
# This should be all of the rails we need
|
7
|
+
class Rails
|
8
|
+
@@env = "draft"
|
9
|
+
def self.env
|
10
|
+
@@env
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.env=(env)
|
14
|
+
@@env = env
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Time
|
19
|
+
def self.zone
|
20
|
+
OpenStruct.new(:now => Time.now)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
ActiveRecord::Base.establish_connection(
|
25
|
+
:adapter => 'sqlite3',
|
26
|
+
:database => File.join(File.dirname(__FILE__), 'has_publishing_test.db')
|
27
|
+
)
|
28
|
+
|
29
|
+
|
30
|
+
class CreateTestModels < ActiveRecord::Migration
|
31
|
+
def self.up
|
32
|
+
if ActiveRecord::Base.connection.table_exists? "test_models"
|
33
|
+
drop_table :test_models
|
34
|
+
end
|
35
|
+
|
36
|
+
create_table :test_models do |t|
|
37
|
+
t.datetime :published_at
|
38
|
+
t.datetime :embargoed_until
|
39
|
+
t.string :kind
|
40
|
+
t.boolean :dirty
|
41
|
+
t.references :published
|
42
|
+
|
43
|
+
t.timestamps
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.down
|
48
|
+
drop_table :test_models
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
CreateTestModels.migrate(:up)
|
53
|
+
|
54
|
+
class TestModel < ActiveRecord::Base
|
55
|
+
has_publishing
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "has_publishing" do
|
59
|
+
|
60
|
+
before do
|
61
|
+
TestModel.delete_all
|
62
|
+
end
|
63
|
+
|
64
|
+
subject do
|
65
|
+
TestModel.new
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "default configuration" do
|
69
|
+
it { HasPublishing.config.scope_records.should be_true }
|
70
|
+
it { HasPublishing.config.published_rails_environment.should eq "production" }
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
describe "scopes" do
|
75
|
+
|
76
|
+
describe "default scope" do
|
77
|
+
context "scope_records is false" do
|
78
|
+
before do
|
79
|
+
HasPublishing.config.scope_records = false
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should not append any conditions" do
|
83
|
+
TestModel.should_not_receive(:where)
|
84
|
+
TestModel.first
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context "rails production is published" do
|
89
|
+
before do
|
90
|
+
HasPublishing.config.scope_records = true
|
91
|
+
HasPublishing.config.published_rails_environment = "production"
|
92
|
+
Rails.env = "production"
|
93
|
+
end
|
94
|
+
|
95
|
+
after do
|
96
|
+
HasPublishing.config.scope_records = false
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should only return published records" do
|
100
|
+
TestModel.should_receive(:published).at_least(:once)
|
101
|
+
TestModel.first
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should not return embargoed records" do
|
105
|
+
TestModel.should_receive(:not_embargoed)
|
106
|
+
TestModel.first
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "draft" do
|
112
|
+
it "should include a draft record" do
|
113
|
+
subject.kind = 'draft'
|
114
|
+
subject.save
|
115
|
+
|
116
|
+
subject.class.draft.should include subject
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should not include a published record" do
|
120
|
+
subject.kind = 'published'
|
121
|
+
subject.save
|
122
|
+
|
123
|
+
subject.class.draft.should_not include subject
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe "published" do
|
128
|
+
it "should include a published record" do
|
129
|
+
subject.kind = 'published'
|
130
|
+
subject.save!
|
131
|
+
subject.class.unscoped.published.should include subject
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should not include a draft record" do
|
135
|
+
subject.kind = 'draft'
|
136
|
+
subject.save
|
137
|
+
subject.class.published.should_not include subject
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "embargoed" do
|
142
|
+
it "should include an embargoed record" do
|
143
|
+
subject.embargoed_until = Time.now - 3600
|
144
|
+
subject.save
|
145
|
+
|
146
|
+
subject.class.embargoed.should include subject
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should not include an embargoed record whose time has not passed" do
|
150
|
+
subject.embargoed_until = Time.now + 5.minutes
|
151
|
+
subject.save
|
152
|
+
subject.class.embargoed.should_not include subject
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
describe "non_embargoed" do
|
157
|
+
it "should include a non embargoed record" do
|
158
|
+
subject.save
|
159
|
+
subject.class.not_embargoed.should include subject
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should include a non-expired embargoed record" do
|
163
|
+
subject.embargoed_until = Time.now - 3600
|
164
|
+
subject.save
|
165
|
+
subject.class.not_embargoed.should include subject
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should not include an embargoed record" do
|
169
|
+
subject.embargoed_until = Time.now + 3600
|
170
|
+
subject.save
|
171
|
+
subject.class.not_embargoed.should_not include subject
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
describe "associations" do
|
177
|
+
it { subject.methods.should include :draft }
|
178
|
+
it { subject.methods.should include :published_id }
|
179
|
+
end
|
180
|
+
|
181
|
+
describe "callbacks" do
|
182
|
+
it "should set the default status to draft" do
|
183
|
+
subject.save
|
184
|
+
subject.kind.should eq "draft"
|
185
|
+
end
|
186
|
+
|
187
|
+
it "should not change the status if it is already set" do
|
188
|
+
subject.kind = "published"
|
189
|
+
subject.save
|
190
|
+
subject.kind.should_not eq "draft"
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should set the record to dirty once it has been created and is already published" do
|
194
|
+
subject.kind = "draft"
|
195
|
+
subject.published = TestModel.new(:updated_at => Time.now)
|
196
|
+
subject.save
|
197
|
+
subject.dirty.should be_true
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
describe "instance methods" do
|
202
|
+
describe "publish!" do
|
203
|
+
before do
|
204
|
+
subject.kind = "draft"
|
205
|
+
subject.save
|
206
|
+
end
|
207
|
+
|
208
|
+
context "first time publishing" do
|
209
|
+
it "should create a published record" do
|
210
|
+
subject.class.should_receive(:create!).and_call_original
|
211
|
+
subject.publish!
|
212
|
+
end
|
213
|
+
|
214
|
+
it "should set the inverse record on the original" do
|
215
|
+
expect {
|
216
|
+
subject.publish!
|
217
|
+
}.to change(subject, :published).to(an_instance_of(TestModel))
|
218
|
+
end
|
219
|
+
|
220
|
+
it "should record the published date" do
|
221
|
+
expect {
|
222
|
+
subject.publish!
|
223
|
+
}.to change(subject, :published_at).to(an_instance_of(Time))
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
describe "withdraw!" do
|
229
|
+
before do
|
230
|
+
subject.kind = 'draft'
|
231
|
+
subject.save
|
232
|
+
end
|
233
|
+
|
234
|
+
it "should not withdraw if the record is not published" do
|
235
|
+
subject.withdraw!.should be_false
|
236
|
+
end
|
237
|
+
|
238
|
+
it "should withdraw the published record" do
|
239
|
+
subject.publish!
|
240
|
+
subject.withdraw!
|
241
|
+
subject.withdrawn?.should be_true
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
describe "draft?" do
|
246
|
+
it { subject.kind = "draft"; subject.draft?.should be_true }
|
247
|
+
it { subject.kind = "published"; subject.draft?.should be_false }
|
248
|
+
end
|
249
|
+
|
250
|
+
describe "published?" do
|
251
|
+
context "record is draft, inverse is published" do
|
252
|
+
before do
|
253
|
+
subject.kind = 'draft'
|
254
|
+
subject.published = TestModel.new(:kind => "published")
|
255
|
+
end
|
256
|
+
|
257
|
+
it { subject.published?.should be_true }
|
258
|
+
end
|
259
|
+
|
260
|
+
context "record is published" do
|
261
|
+
it { subject.kind = "published"; subject.published?.should be_true }
|
262
|
+
it { subject.kind = "draft"; subject.published?.should be_false }
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
describe "ever_published?" do
|
267
|
+
before do
|
268
|
+
subject.save
|
269
|
+
end
|
270
|
+
|
271
|
+
context "record is published" do
|
272
|
+
before do
|
273
|
+
subject.publish!
|
274
|
+
end
|
275
|
+
|
276
|
+
it { subject.ever_published?.should be_true }
|
277
|
+
end
|
278
|
+
|
279
|
+
context "record is withdrawn" do
|
280
|
+
before do
|
281
|
+
subject.publish!
|
282
|
+
subject.withdraw!
|
283
|
+
end
|
284
|
+
|
285
|
+
it { subject.ever_published?.should be_true }
|
286
|
+
end
|
287
|
+
|
288
|
+
it { subject.ever_published?.should be_false }
|
289
|
+
end
|
290
|
+
|
291
|
+
describe "under_embargo?" do
|
292
|
+
before do
|
293
|
+
subject.publish!
|
294
|
+
end
|
295
|
+
|
296
|
+
context "record is published and embargo has finished" do
|
297
|
+
before do
|
298
|
+
subject.embargoed_until = Time.now + 5.minutes
|
299
|
+
subject.save
|
300
|
+
end
|
301
|
+
|
302
|
+
it { subject.under_embargo?.should be_true }
|
303
|
+
end
|
304
|
+
|
305
|
+
context "embargo is not present" do
|
306
|
+
it { subject.under_embargo?.should be_false }
|
307
|
+
end
|
308
|
+
|
309
|
+
it { subject.under_embargo?.should be_false }
|
310
|
+
end
|
311
|
+
|
312
|
+
describe "withdrawn?" do
|
313
|
+
context "record is draft, inverse is withdrawn" do
|
314
|
+
before do
|
315
|
+
subject.kind = 'draft'
|
316
|
+
subject.published = TestModel.new(:kind => "withdrawn")
|
317
|
+
end
|
318
|
+
|
319
|
+
it { subject.withdrawn?.should be_true }
|
320
|
+
end
|
321
|
+
|
322
|
+
context "record is withdrawn" do
|
323
|
+
it { subject.kind = "withdrawn"; subject.withdrawn?.should be_true }
|
324
|
+
it { subject.kind = "draft"; subject.withdrawn?.should be_false }
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
metadata
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: has_publishing
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Josh McArthur @ 3months
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-12-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activerecord
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: activesupport
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec-rails
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: sqlite3
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
description: Add ability to draft/publish/withdraw/embargo models
|
79
|
+
email:
|
80
|
+
- joshua.mcarthur@gmail.com
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files: []
|
84
|
+
files:
|
85
|
+
- .gitignore
|
86
|
+
- .rspec
|
87
|
+
- Gemfile
|
88
|
+
- LICENSE.txt
|
89
|
+
- README.md
|
90
|
+
- Rakefile
|
91
|
+
- has_publishing.gemspec
|
92
|
+
- lib/has_publishing.rb
|
93
|
+
- lib/has_publishing/class_methods.rb
|
94
|
+
- lib/has_publishing/configuration.rb
|
95
|
+
- lib/has_publishing/generators/publishing_generator.rb
|
96
|
+
- lib/has_publishing/generators/templates/migration.rb.erb
|
97
|
+
- lib/has_publishing/instance_methods.rb
|
98
|
+
- lib/has_publishing/version.rb
|
99
|
+
- spec/has_publishing_spec.rb
|
100
|
+
homepage: https://github.com/3months/has_publishing
|
101
|
+
licenses: []
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options: []
|
104
|
+
require_paths:
|
105
|
+
- lib
|
106
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
107
|
+
none: false
|
108
|
+
requirements:
|
109
|
+
- - ! '>='
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
segments:
|
113
|
+
- 0
|
114
|
+
hash: -2118166424192642894
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
+
none: false
|
117
|
+
requirements:
|
118
|
+
- - ! '>='
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
segments:
|
122
|
+
- 0
|
123
|
+
hash: -2118166424192642894
|
124
|
+
requirements: []
|
125
|
+
rubyforge_project:
|
126
|
+
rubygems_version: 1.8.24
|
127
|
+
signing_key:
|
128
|
+
specification_version: 3
|
129
|
+
summary: Add publishing to your ActiveRecord models
|
130
|
+
test_files:
|
131
|
+
- spec/has_publishing_spec.rb
|