has_publishing 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ spec/has_publishing_test.db
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in has_publishing.gemspec
4
+ gemspec
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,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -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,7 @@
1
+ module HasPublishing
2
+ include ActiveSupport::Configurable
3
+
4
+ # Set some default config
5
+ self.config.scope_records = true
6
+ self.config.published_rails_environment = "production"
7
+ 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,3 @@
1
+ module HasPublishing
2
+ VERSION = "0.0.1"
3
+ 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