drafter 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+ gem "activerecord"
6
+ gem "sqlite3"
7
+ gem "debugger"
8
+
9
+ # Add dependencies to develop your gem here.
10
+ # Include everything needed to run rake, tests, features, etc.
11
+ group :development do
12
+ gem "minitest", ">= 0"
13
+ gem "bundler", "~> 1.0.0"
14
+ gem "jeweler", "~> 1.6.4"
15
+ gem "rcov", ">= 0"
16
+ gem 'turn'
17
+ gem 'carrierwave'
18
+ gem "minitest-rails-shoulda"
19
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,127 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ actionmailer (3.2.8)
5
+ actionpack (= 3.2.8)
6
+ mail (~> 2.4.4)
7
+ actionpack (3.2.8)
8
+ activemodel (= 3.2.8)
9
+ activesupport (= 3.2.8)
10
+ builder (~> 3.0.0)
11
+ erubis (~> 2.7.0)
12
+ journey (~> 1.0.4)
13
+ rack (~> 1.4.0)
14
+ rack-cache (~> 1.2)
15
+ rack-test (~> 0.6.1)
16
+ sprockets (~> 2.1.3)
17
+ activemodel (3.2.8)
18
+ activesupport (= 3.2.8)
19
+ builder (~> 3.0.0)
20
+ activerecord (3.2.8)
21
+ activemodel (= 3.2.8)
22
+ activesupport (= 3.2.8)
23
+ arel (~> 3.0.2)
24
+ tzinfo (~> 0.3.29)
25
+ activeresource (3.2.8)
26
+ activemodel (= 3.2.8)
27
+ activesupport (= 3.2.8)
28
+ activesupport (3.2.8)
29
+ i18n (~> 0.6)
30
+ multi_json (~> 1.0)
31
+ ansi (1.4.2)
32
+ arel (3.0.2)
33
+ builder (3.0.0)
34
+ carrierwave (0.6.2)
35
+ activemodel (>= 3.2.0)
36
+ activesupport (>= 3.2.0)
37
+ columnize (0.3.6)
38
+ debugger (1.1.1)
39
+ columnize (>= 0.3.1)
40
+ debugger-linecache (~> 1.1)
41
+ debugger-ruby_core_source (~> 1.1)
42
+ debugger-linecache (1.1.1)
43
+ debugger-ruby_core_source (>= 1.1.1)
44
+ debugger-ruby_core_source (1.1.2)
45
+ erubis (2.7.0)
46
+ git (1.2.5)
47
+ hike (1.2.1)
48
+ i18n (0.6.0)
49
+ jeweler (1.6.4)
50
+ bundler (~> 1.0)
51
+ git (>= 1.2.5)
52
+ rake
53
+ journey (1.0.4)
54
+ json (1.7.4)
55
+ mail (2.4.4)
56
+ i18n (>= 0.4.0)
57
+ mime-types (~> 1.16)
58
+ treetop (~> 1.4.8)
59
+ mime-types (1.19)
60
+ minitest (3.2.0)
61
+ minitest-matchers (1.1.3)
62
+ minitest (>= 2.5.0)
63
+ minitest-rails (0.1.3)
64
+ minitest (~> 3.0)
65
+ rails (~> 3.0)
66
+ minitest-rails-shoulda (0.1.0)
67
+ minitest-matchers (~> 1.1.3)
68
+ minitest-rails (~> 0.1.0)
69
+ shoulda-matchers (~> 1.2.0)
70
+ multi_json (1.3.6)
71
+ polyglot (0.3.3)
72
+ rack (1.4.1)
73
+ rack-cache (1.2)
74
+ rack (>= 0.4)
75
+ rack-ssl (1.3.2)
76
+ rack
77
+ rack-test (0.6.1)
78
+ rack (>= 1.0)
79
+ rails (3.2.8)
80
+ actionmailer (= 3.2.8)
81
+ actionpack (= 3.2.8)
82
+ activerecord (= 3.2.8)
83
+ activeresource (= 3.2.8)
84
+ activesupport (= 3.2.8)
85
+ bundler (~> 1.0)
86
+ railties (= 3.2.8)
87
+ railties (3.2.8)
88
+ actionpack (= 3.2.8)
89
+ activesupport (= 3.2.8)
90
+ rack-ssl (~> 1.3.2)
91
+ rake (>= 0.8.7)
92
+ rdoc (~> 3.4)
93
+ thor (>= 0.14.6, < 2.0)
94
+ rake (0.9.2.2)
95
+ rcov (0.9.11)
96
+ rdoc (3.12)
97
+ json (~> 1.4)
98
+ shoulda-matchers (1.2.0)
99
+ activesupport (>= 3.0.0)
100
+ sprockets (2.1.3)
101
+ hike (~> 1.2)
102
+ rack (~> 1.0)
103
+ tilt (~> 1.1, != 1.3.0)
104
+ sqlite3 (1.3.6)
105
+ thor (0.16.0)
106
+ tilt (1.3.3)
107
+ treetop (1.4.10)
108
+ polyglot
109
+ polyglot (>= 0.3.1)
110
+ turn (0.9.5)
111
+ ansi
112
+ tzinfo (0.3.33)
113
+
114
+ PLATFORMS
115
+ ruby
116
+
117
+ DEPENDENCIES
118
+ activerecord
119
+ bundler (~> 1.0.0)
120
+ carrierwave
121
+ debugger
122
+ jeweler (~> 1.6.4)
123
+ minitest
124
+ minitest-rails-shoulda
125
+ rcov
126
+ sqlite3
127
+ turn
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 futurechimp
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,19 @@
1
+ = drafter
2
+
3
+ Description goes here.
4
+
5
+ == Contributing to drafter
6
+
7
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
8
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
9
+ * Fork the project
10
+ * Start a feature/bugfix branch
11
+ * Commit and push until you are happy with your contribution
12
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2012 futurechimp. See LICENSE.txt for
18
+ further details.
19
+
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "drafter"
18
+ gem.homepage = "http://github.com/futurechimp/drafter"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Simple}
21
+ gem.description = %Q{A}
22
+ gem.email = "dave.hrycyszyn@headlondon.com"
23
+ gem.authors = ["futurechimp"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+ require 'rcov/rcovtask'
36
+ Rcov::RcovTask.new do |test|
37
+ test.libs << 'test'
38
+ test.pattern = 'test/**/test_*.rb'
39
+ test.verbose = true
40
+ test.rcov_opts << '--exclude "gems/*"'
41
+ end
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "drafter #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/drafter.gemspec ADDED
@@ -0,0 +1,93 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "drafter"
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["futurechimp"]
12
+ s.date = "2012-08-17"
13
+ s.description = "A"
14
+ s.email = "dave.hrycyszyn@headlondon.com"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE.txt",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "drafter.gemspec",
28
+ "lib/drafter.rb",
29
+ "lib/drafter/creation.rb",
30
+ "lib/drafter/draft.rb",
31
+ "lib/drafter/draft_upload.rb",
32
+ "lib/drafter/draft_uploader.rb",
33
+ "lib/drafter/draftable.rb",
34
+ "test/drafter/test_creation.rb",
35
+ "test/drafter/test_draft.rb",
36
+ "test/drafter/test_draft_upload.rb",
37
+ "test/drafter/test_draftable.rb",
38
+ "test/fixtures/bar.txt",
39
+ "test/fixtures/foo.txt",
40
+ "test/helper.rb",
41
+ "test/support/data.rb",
42
+ "test/support/models.rb",
43
+ "test/support/schema.rb",
44
+ "test/support/uploader.rb",
45
+ "test/test_drafter.rb",
46
+ "test/watchr.rb"
47
+ ]
48
+ s.homepage = "http://github.com/futurechimp/drafter"
49
+ s.licenses = ["MIT"]
50
+ s.require_paths = ["lib"]
51
+ s.rubygems_version = "1.8.17"
52
+ s.summary = "Simple"
53
+
54
+ if s.respond_to? :specification_version then
55
+ s.specification_version = 3
56
+
57
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
58
+ s.add_runtime_dependency(%q<activerecord>, [">= 0"])
59
+ s.add_runtime_dependency(%q<sqlite3>, [">= 0"])
60
+ s.add_runtime_dependency(%q<debugger>, [">= 0"])
61
+ s.add_development_dependency(%q<minitest>, [">= 0"])
62
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
63
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
64
+ s.add_development_dependency(%q<rcov>, [">= 0"])
65
+ s.add_development_dependency(%q<turn>, [">= 0"])
66
+ s.add_development_dependency(%q<carrierwave>, [">= 0"])
67
+ s.add_development_dependency(%q<minitest-rails-shoulda>, [">= 0"])
68
+ else
69
+ s.add_dependency(%q<activerecord>, [">= 0"])
70
+ s.add_dependency(%q<sqlite3>, [">= 0"])
71
+ s.add_dependency(%q<debugger>, [">= 0"])
72
+ s.add_dependency(%q<minitest>, [">= 0"])
73
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
74
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
75
+ s.add_dependency(%q<rcov>, [">= 0"])
76
+ s.add_dependency(%q<turn>, [">= 0"])
77
+ s.add_dependency(%q<carrierwave>, [">= 0"])
78
+ s.add_dependency(%q<minitest-rails-shoulda>, [">= 0"])
79
+ end
80
+ else
81
+ s.add_dependency(%q<activerecord>, [">= 0"])
82
+ s.add_dependency(%q<sqlite3>, [">= 0"])
83
+ s.add_dependency(%q<debugger>, [">= 0"])
84
+ s.add_dependency(%q<minitest>, [">= 0"])
85
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
86
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
87
+ s.add_dependency(%q<rcov>, [">= 0"])
88
+ s.add_dependency(%q<turn>, [">= 0"])
89
+ s.add_dependency(%q<carrierwave>, [">= 0"])
90
+ s.add_dependency(%q<minitest-rails-shoulda>, [">= 0"])
91
+ end
92
+ end
93
+
@@ -0,0 +1,57 @@
1
+ module Drafter
2
+
3
+ # Takes care of associating and creating Draft objects for the draftable
4
+ # class.
5
+ module Creation
6
+ extend ActiveSupport::Concern
7
+
8
+ # Set up the association to the Draft object.
9
+ included do
10
+ has_one :draft, :as => :draftable
11
+ end
12
+
13
+ # Build and save the draft when told to do so.
14
+ def save_draft
15
+ if self.valid?
16
+ attrs = self.attributes
17
+ uploads = build_draft_uploads(attrs)
18
+ self.build_draft(:data => attrs)
19
+ self.draft.save!
20
+ self.draft.draft_uploads << uploads
21
+ self.draft
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ # Loop through and create DraftUpload objects for any Carrierwave
28
+ # uploaders mounted on this draftable object.
29
+ #
30
+ # @param [Hash] attrs the attributes to loop through
31
+ # @return [Array<DraftUpload>] an array of unsaved DraftUpload objects.
32
+ def build_draft_uploads(attrs)
33
+ draft_uploads = []
34
+ attrs.keys.each do |key|
35
+ if self.send(key).is_a?(CarrierWave::Uploader::Base)
36
+ draft_uploads << build_draft_upload(key)
37
+ end
38
+ end
39
+ draft_uploads
40
+ end
41
+
42
+ # Get a reference to the CarrierWave uploader mounted on the
43
+ # current draftable object, grab the file in it, and shove
44
+ # that file into a new DraftUpload.
45
+ #
46
+ # @param [String] key the attribute where the CarrierWave uploader
47
+ # is mounted.
48
+ # @return [DraftUpload] containing the file in the uploader.
49
+ def build_draft_upload(key)
50
+ cw_uploader = self.send(key)
51
+ file = File.new(cw_uploader.file.path) if cw_uploader.file
52
+ draft_upload = DraftUpload.new(
53
+ :file_data => file, :draftable_mount_column => key)
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,92 @@
1
+ # A model to store serialized draft data. Drafts attach themselves
2
+ # polymorphically to any other ActiveRecord class, loop through the
3
+ # target draftable classes's attributes, and store them as a Hash
4
+ # in the :data text field in the database.
5
+ #
6
+ # Drafts can also keep track of the target draftable's CarrierWave uploads,
7
+ # if any exist.
8
+ #
9
+ class Draft < ActiveRecord::Base
10
+
11
+ # Validations
12
+ #
13
+ validates_presence_of :data
14
+
15
+ # Associations
16
+ #
17
+ has_many :draft_uploads
18
+ belongs_to :draftable, :polymorphic => true
19
+
20
+
21
+ # Store serialized data for the associated draftable as a Hash of
22
+ # attributes.
23
+ #
24
+ serialize :data, Hash
25
+
26
+ # Approve a draft, setting the attributes of the draftable object to
27
+ # contain the draft content, saving the draftable, and destroying the draft.
28
+ #
29
+ def approve!
30
+ draftable = build_draftable
31
+ draftable.save!
32
+ self.destroy
33
+ draftable
34
+ end
35
+
36
+ # Set things up so we can use dot notation to access draft data, e.g.
37
+ # we can do either @foo.draft.data["title"] or (more neatly) we can
38
+ # do @foo.draft.title
39
+ #
40
+ def method_missing(meth, *args, &block)
41
+ if self.data.keys.include?(meth.to_s)
42
+ self.data[meth.to_s]
43
+ else
44
+ super
45
+ end
46
+ end
47
+
48
+
49
+ private
50
+
51
+ # Find the existing draftable, or build a new one, and set it up.
52
+ #
53
+ # @return the existing draftable object, or a new one of the proper
54
+ # type, with attributes and files hanging off it.
55
+ def build_draftable
56
+ dr = draftable.nil? ? self.draftable_type.constantize.new : draftable
57
+ restore_attrs_on(dr)
58
+ restore_files_on(dr)
59
+ dr
60
+ end
61
+
62
+ # Whack the draft data onto the real object.
63
+ #
64
+ # @param [Draftable] dr a new or existing draftable to restore from the
65
+ # draft attributes.
66
+ # @return [Draftable] the draftable object populated with the draft attrs.
67
+ def restore_attrs_on(dr)
68
+ draftable_columns.each do |key|
69
+ dr.raw_write_attribute key, self.data[key]
70
+ end
71
+ dr
72
+ end
73
+
74
+ # Attach draft files to the real object.
75
+ #
76
+ # @param [Draftable] dr a new or existing draftable.
77
+ # @return [Draftable] the draftable object where CarrierWave uploads
78
+ # on the object have been replaced with their draft equivalents.
79
+ def restore_files_on(dr)
80
+ draft_uploads.each do |draft_upload|
81
+ dr.send(draft_upload.draftable_mount_column + "=", draft_upload.file_data)
82
+ end
83
+ end
84
+
85
+ # We don't want to copy all the draft's columns into the draftable
86
+ # objects attributes.
87
+ #
88
+ # @return [Array] the draft's columns minus :id, :created_at, :updated_at
89
+ def draftable_columns
90
+ self.data.keys - ['id', 'created_at', 'updated_at']
91
+ end
92
+ end
@@ -0,0 +1,27 @@
1
+ # Some of the objects we want to keep drafts of will have attached uploads,
2
+ # which are stored using CarrierWave. The simplest thing which can work, I
3
+ # think, is to grab the uploaded data from the draftable object, and store it
4
+ # in its own class, making a note of the original mount column that the file
5
+ # was destined for.
6
+ #
7
+ # The original :file_data is saved in this model, without any processing
8
+ # or modification. When the draft is approved, the Draft will push the
9
+ # :file_data onto the :draftable_mount_column of the draftable object.
10
+ #
11
+ class DraftUpload < ActiveRecord::Base
12
+
13
+ # Associations
14
+ #
15
+ belongs_to :draft
16
+
17
+ # Validations
18
+ #
19
+ validates_presence_of :draft
20
+ validates_presence_of :draftable_mount_column
21
+ validates_presence_of :file_data
22
+
23
+ # Macros
24
+ #
25
+ mount_uploader :file_data, DraftUploader
26
+
27
+ end
@@ -0,0 +1,8 @@
1
+ # This CarrierWave uploader is used to steal incoming files off of
2
+ # draftable objects, and store them on a DraftUpload for later use.
3
+ #
4
+ class DraftUploader < CarrierWave::Uploader::Base
5
+
6
+ storage :file
7
+
8
+ end
@@ -0,0 +1,25 @@
1
+ module Drafter
2
+ # Adds a flag to determine whether a model class is draftable.
3
+ module Draftable
4
+ extend ActiveSupport::Concern
5
+
6
+ # Overrides the +draftable+ method to first define the +draftable?+ class method before
7
+ # deferring to the original +draftable+.
8
+ module ClassMethods
9
+ def draftable(*args)
10
+ class << self
11
+ def draftable?
12
+ true
13
+ end
14
+ end
15
+ end
16
+
17
+ # For all ActiveRecord::Base models that do not call the +draftable+ method, the +draftable?+
18
+ # method will return false.
19
+ def draftable?
20
+ false
21
+ end
22
+ end
23
+
24
+ end
25
+ end
data/lib/drafter.rb ADDED
@@ -0,0 +1,29 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/dependencies/autoload'
3
+ require 'active_support/core_ext/module/delegation'
4
+ require 'active_record'
5
+
6
+ require 'lib/drafter/draft_uploader'
7
+
8
+ module Drafter
9
+ extend ActiveSupport::Concern
10
+ extend ActiveSupport::Autoload
11
+
12
+ autoload :Creation
13
+ autoload :Draft
14
+ autoload :Draftable
15
+ autoload :DraftUpload
16
+
17
+ class << self
18
+ delegate :config, :configure, :to => Draft
19
+ delegate :config, :configure, :to => DraftUpload
20
+ end
21
+
22
+ included do
23
+ include Creation
24
+ include Draftable
25
+ end
26
+
27
+ end
28
+
29
+ ActiveRecord::Base.class_eval{ include Drafter }
@@ -0,0 +1,140 @@
1
+ require 'helper'
2
+
3
+ class TestCreation < Minitest::Unit::TestCase
4
+
5
+ describe "Saving a draft of" do
6
+ describe "a new draftable article" do
7
+ before do
8
+ @article_count = Article.count
9
+ @draft_count = Draft.count
10
+ @draft_upload_count = DraftUpload.count
11
+ @article = Article.new(
12
+ :text => "original text",
13
+ :upload => file_upload
14
+ )
15
+ @draft = @article.save_draft
16
+ end
17
+
18
+ it "should not create a new draftable" do
19
+ assert_equal(@article_count, Article.count)
20
+ end
21
+
22
+ it "should create a saved draft object" do
23
+ assert_equal(@draft_count + 1, Draft.count)
24
+ end
25
+
26
+ it "should return the @draft object" do
27
+ assert_equal(Draft, @draft.class)
28
+ end
29
+
30
+ it "should save the contents of the :text attribute" do
31
+ assert_equal "original text", @draft.data["text"]
32
+ end
33
+
34
+ it "should create a DraftUpload object" do
35
+ assert_equal(@draft_upload_count + 1, DraftUpload.count)
36
+ end
37
+
38
+ it "should add the draft_upload to the Draft's array of them" do
39
+ assert_equal(1, @draft.draft_uploads.length)
40
+ assert_equal(DraftUpload.last, @draft.draft_uploads.first)
41
+ end
42
+ end
43
+
44
+ describe "an existing draftable article" do
45
+ before do
46
+ @draft_count = Draft.count
47
+ @draft_upload_count = DraftUpload.count
48
+ @article = Article.create!(:text => "original text")
49
+ @article.text = "draft text"
50
+ end
51
+
52
+ describe "with a file upload" do
53
+ before do
54
+ @article.upload = file_upload
55
+ @draft = @article.save_draft
56
+ end
57
+
58
+ describe "the article" do
59
+ it "should still have text of 'original text'" do
60
+ assert_equal("original text", @article.reload.text)
61
+ end
62
+
63
+ it "should have a draft attached" do
64
+ assert_equal(Draft, @article.draft.class)
65
+ end
66
+
67
+ it "should create a new draft" do
68
+ assert_equal(@draft_count + 1, Draft.count)
69
+ end
70
+ end
71
+
72
+ describe "the article's draft" do
73
+ it "has a serialized :text attribute with content 'draft text'" do
74
+ assert_equal('draft text', @article.draft.data["text"])
75
+ end
76
+
77
+ it "should create a DraftUpload object" do
78
+ assert_equal(@draft_upload_count + 1, DraftUpload.count)
79
+ end
80
+
81
+ it "should add the draft_upload to the Draft's array of them" do
82
+ assert_equal(1, @draft.draft_uploads.length)
83
+ assert_equal(DraftUpload.last, @draft.draft_uploads.first)
84
+ end
85
+ end
86
+ end
87
+
88
+
89
+ describe "without a file upload" do
90
+ before do
91
+ @draft = @article.save_draft
92
+ end
93
+
94
+ describe "the article" do
95
+ it "should still have text of 'original text'" do
96
+ assert_equal("original text", @article.reload.text)
97
+ end
98
+
99
+ it "should have a draft attached" do
100
+ assert_equal(Draft, @article.draft.class)
101
+ end
102
+
103
+ it "should create a new draft" do
104
+ assert_equal(@draft_count + 1, Draft.count)
105
+ end
106
+ end
107
+
108
+ describe "the article's draft" do
109
+ it "has a serialized :text attribute with content 'draft text'" do
110
+ assert_equal('draft text', @article.draft.data["text"])
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ describe "an invalid article" do
117
+ before do
118
+ @draft_count = Draft.count
119
+ @draft_upload_count = DraftUpload.count
120
+ @article = Article.create!(:text => "original text")
121
+ @article.text = ""
122
+ @article.upload = file_upload
123
+ @draft = @article.save_draft
124
+ end
125
+
126
+ it "should not create a Draft object" do
127
+ assert_equal(@draft_count, Draft.count)
128
+ end
129
+
130
+ it "should not create a DraftUpload object" do
131
+ assert_equal(@draft_upload_count, DraftUpload.count)
132
+ end
133
+
134
+ it "sets up validation errors on the @article" do
135
+ assert_equal(1, @article.errors.count)
136
+ end
137
+ end
138
+ end
139
+
140
+ end
@@ -0,0 +1,104 @@
1
+ require 'helper'
2
+
3
+ class TestDraft < Minitest::Unit::TestCase
4
+
5
+ describe Draft do
6
+ subject { @draft = Draft.new }
7
+ describe "validations" do
8
+ it { must validate_presence_of(:data) }
9
+ end
10
+
11
+ describe "associations" do
12
+ it { must belong_to(:draftable) }
13
+ it { must have_many(:draft_uploads) }
14
+ end
15
+ end
16
+
17
+ describe "Accessing draft data properties" do
18
+ before do
19
+ @draft = Draft.new(:data => {"foo" => "bar"})
20
+ end
21
+
22
+ describe "through the data hash" do
23
+ it "works" do
24
+ assert_equal("bar", @draft.data["foo"])
25
+ end
26
+ end
27
+
28
+ describe "using dot notation" do
29
+ it "also works" do
30
+ assert_equal("bar", @draft.foo)
31
+ end
32
+ end
33
+ end
34
+
35
+ describe "Approving a draft" do
36
+ before do
37
+ @article = Article.new(
38
+ :text => "initial text",
39
+ :upload => file_upload)
40
+ end
41
+
42
+ describe "for an article which hasn't yet been saved" do
43
+ before do
44
+ @article_count = Article.count
45
+ @draft = @article.save_draft
46
+ @draft_count = Draft.count
47
+ @article = @draft.approve!
48
+ end
49
+
50
+ it "should create an article" do
51
+ assert_equal(@article_count + 1, Article.count)
52
+ end
53
+
54
+ it "should return the saved article" do
55
+ assert_equal(Article, @article.class)
56
+ end
57
+
58
+ it "should properly populate all the attributes" do
59
+ assert_equal("initial text", @article.text)
60
+ end
61
+
62
+ it "should populate all the file uploads" do
63
+ assert_equal("foo.txt", @article.upload.filename)
64
+ assert_equal("foo foo foo", File.open(@article.upload.path).read)
65
+ end
66
+
67
+ it "should delete the article's draft" do
68
+ assert_equal(@draft_count - 1, Draft.count)
69
+ refute @article.draft
70
+ end
71
+ end
72
+
73
+ describe "for an article which already exists" do
74
+ before do
75
+ @article.save
76
+ @article_count = Article.count
77
+ @article.text = "some draft text"
78
+ @article.upload = file_upload("bar.txt")
79
+ @draft = @article.save_draft
80
+ @draft_count = Draft.count
81
+ @draft.approve!
82
+ end
83
+
84
+ it "shouldn't do anything mental, like creating a new object" do
85
+ assert_equal(@article_count, Article.count)
86
+ end
87
+
88
+ it "should properly populate all the attributes" do
89
+ assert_equal("some draft text", @article.text)
90
+ end
91
+
92
+ it "should populate all the file uploads" do
93
+ assert_equal("bar.txt", @article.upload.filename)
94
+ assert_equal("bar bar bar", File.open(@article.upload.path).read)
95
+ end
96
+
97
+ it "should delete the article's draft" do
98
+ assert_equal(@draft_count - 1, Draft.count)
99
+ refute @article.reload.draft
100
+ end
101
+ end
102
+ end
103
+
104
+ end
@@ -0,0 +1,31 @@
1
+ require 'helper'
2
+
3
+ class TestDraftUpload < Minitest::Unit::TestCase
4
+
5
+ describe DraftUpload do
6
+ subject { DraftUpload.new }
7
+
8
+ describe "validations" do
9
+ it { must validate_presence_of :file_data }
10
+ it { must validate_presence_of :draft }
11
+ it { must validate_presence_of :draftable_mount_column }
12
+ end
13
+
14
+ describe "associations" do
15
+ it { must belong_to :draft }
16
+ end
17
+
18
+ describe "uploading files" do
19
+ it "works" do
20
+ draft_upload = DraftUpload.new(
21
+ :file_data => file_upload,
22
+ :draft => Draft.create(:data => {:foo => :bar}),
23
+ :draftable_mount_column => :photo
24
+ )
25
+ assert draft_upload.save!
26
+ assert File.exist?(draft_upload.reload.file_data.path)
27
+ end
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,17 @@
1
+ require 'helper'
2
+
3
+ class TestDraftable < MiniTest::Unit::TestCase
4
+
5
+ describe "A draftable ActiveRecord class" do
6
+ it "should know it's draftable" do
7
+ assert Article.draftable?
8
+ end
9
+ end
10
+
11
+ describe "A non-draftable ActiveRecord class" do
12
+ it "should know it's not draftable" do
13
+ refute Comment.draftable?
14
+ end
15
+ end
16
+
17
+ end
@@ -0,0 +1 @@
1
+ bar bar bar
@@ -0,0 +1 @@
1
+ foo foo foo
data/test/helper.rb ADDED
@@ -0,0 +1,58 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'minitest/spec'
11
+ require 'debugger'
12
+
13
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
14
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
15
+
16
+ require 'active_record'
17
+ require 'carrierwave'
18
+ require 'carrierwave/orm/activerecord'
19
+ require 'drafter'
20
+ require 'turn'
21
+ require 'minitest/matchers'
22
+ require "shoulda/matchers/active_record"
23
+ require "shoulda/matchers/active_model"
24
+
25
+ # Establish a connection to our test database
26
+ ActiveRecord::Base.establish_connection(
27
+ :adapter => "sqlite3",
28
+ :database => File.dirname(__FILE__) + "/drafter.sqlite3")
29
+
30
+ load File.dirname(__FILE__) + '/support/schema.rb'
31
+ load File.dirname(__FILE__) + '/support/uploader.rb'
32
+ load File.dirname(__FILE__) + '/support/models.rb'
33
+ load File.dirname(__FILE__) + '/support/data.rb'
34
+
35
+ class MiniTest::Unit::TestCase
36
+
37
+ # Pull in shoulda matchers for minitest.
38
+ #
39
+ # You need to have the minitest-matchers gem and the minitest-rails-shoulda gem
40
+ # for this to work.
41
+ include Shoulda::Matchers::ActiveRecord
42
+ extend Shoulda::Matchers::ActiveRecord
43
+ include Shoulda::Matchers::ActiveModel
44
+ extend Shoulda::Matchers::ActiveModel
45
+
46
+ def file_upload(name="foo.txt")
47
+ File.new(File.dirname(__FILE__) + "/fixtures/#{name}")
48
+ end
49
+
50
+ end
51
+
52
+ MiniTest::Unit.autorun
53
+
54
+ Turn.config do |c|
55
+ c.format = :outline
56
+ c.trace = false
57
+ c.natural = true
58
+ end
@@ -0,0 +1,2 @@
1
+ # Creates an article so we've got a bit of test data.
2
+ Article.create!(:text => "foo")
@@ -0,0 +1,17 @@
1
+ # Define models we can test with
2
+
3
+ # A draftable article class
4
+ class Article < ActiveRecord::Base
5
+
6
+ validates_presence_of :text
7
+
8
+ draftable
9
+
10
+ mount_uploader :upload, Uploader
11
+
12
+ end
13
+
14
+
15
+ # A non-draftable comment class
16
+ class Comment < ActiveRecord::Base
17
+ end
@@ -0,0 +1,27 @@
1
+ # Load up a database schema and force schema creation
2
+ ActiveRecord::Schema.define do
3
+ self.verbose = false
4
+
5
+ create_table :articles, :force => true do |t|
6
+ t.string :text
7
+ t.string :upload
8
+ t.timestamps
9
+ end
10
+
11
+ create_table :drafts, :force => true do |t|
12
+ t.text :data
13
+ t.references :draftable, :polymorphic => true
14
+ end
15
+
16
+ change_table :drafts do |t|
17
+ t.index [:draftable_id, :draftable_type]
18
+ t.index :data
19
+ end
20
+
21
+ create_table :draft_uploads, :force => true do |t|
22
+ t.references :draft
23
+ t.string :draftable_mount_column
24
+ t.string :file_data
25
+ end
26
+
27
+ end
@@ -0,0 +1,8 @@
1
+ class Uploader < CarrierWave::Uploader::Base
2
+
3
+ ##
4
+ # Storage type
5
+ #
6
+ storage :file
7
+
8
+ end
@@ -0,0 +1,5 @@
1
+ require 'helper'
2
+
3
+ class TestDrafter < MiniTest::Unit::TestCase
4
+
5
+ end
data/test/watchr.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'autowatchr'
2
+
3
+ Autowatchr.new(self) do |config|
4
+ config.failing_only = true
5
+ end
metadata ADDED
@@ -0,0 +1,187 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: drafter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - futurechimp
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-17 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: &2160790860 !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: *2160790860
25
+ - !ruby/object:Gem::Dependency
26
+ name: sqlite3
27
+ requirement: &2160778900 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *2160778900
36
+ - !ruby/object:Gem::Dependency
37
+ name: debugger
38
+ requirement: &2160777900 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *2160777900
47
+ - !ruby/object:Gem::Dependency
48
+ name: minitest
49
+ requirement: &2160776580 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *2160776580
58
+ - !ruby/object:Gem::Dependency
59
+ name: bundler
60
+ requirement: &2160774900 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: 1.0.0
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *2160774900
69
+ - !ruby/object:Gem::Dependency
70
+ name: jeweler
71
+ requirement: &2160773980 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: 1.6.4
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *2160773980
80
+ - !ruby/object:Gem::Dependency
81
+ name: rcov
82
+ requirement: &2160773220 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *2160773220
91
+ - !ruby/object:Gem::Dependency
92
+ name: turn
93
+ requirement: &2160772480 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *2160772480
102
+ - !ruby/object:Gem::Dependency
103
+ name: carrierwave
104
+ requirement: &2160771780 !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: *2160771780
113
+ - !ruby/object:Gem::Dependency
114
+ name: minitest-rails-shoulda
115
+ requirement: &2160771260 !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ! '>='
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ type: :development
122
+ prerelease: false
123
+ version_requirements: *2160771260
124
+ description: A
125
+ email: dave.hrycyszyn@headlondon.com
126
+ executables: []
127
+ extensions: []
128
+ extra_rdoc_files:
129
+ - LICENSE.txt
130
+ - README.rdoc
131
+ files:
132
+ - .document
133
+ - Gemfile
134
+ - Gemfile.lock
135
+ - LICENSE.txt
136
+ - README.rdoc
137
+ - Rakefile
138
+ - VERSION
139
+ - drafter.gemspec
140
+ - lib/drafter.rb
141
+ - lib/drafter/creation.rb
142
+ - lib/drafter/draft.rb
143
+ - lib/drafter/draft_upload.rb
144
+ - lib/drafter/draft_uploader.rb
145
+ - lib/drafter/draftable.rb
146
+ - test/drafter/test_creation.rb
147
+ - test/drafter/test_draft.rb
148
+ - test/drafter/test_draft_upload.rb
149
+ - test/drafter/test_draftable.rb
150
+ - test/fixtures/bar.txt
151
+ - test/fixtures/foo.txt
152
+ - test/helper.rb
153
+ - test/support/data.rb
154
+ - test/support/models.rb
155
+ - test/support/schema.rb
156
+ - test/support/uploader.rb
157
+ - test/test_drafter.rb
158
+ - test/watchr.rb
159
+ homepage: http://github.com/futurechimp/drafter
160
+ licenses:
161
+ - MIT
162
+ post_install_message:
163
+ rdoc_options: []
164
+ require_paths:
165
+ - lib
166
+ required_ruby_version: !ruby/object:Gem::Requirement
167
+ none: false
168
+ requirements:
169
+ - - ! '>='
170
+ - !ruby/object:Gem::Version
171
+ version: '0'
172
+ segments:
173
+ - 0
174
+ hash: 3420985019024950732
175
+ required_rubygems_version: !ruby/object:Gem::Requirement
176
+ none: false
177
+ requirements:
178
+ - - ! '>='
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ requirements: []
182
+ rubyforge_project:
183
+ rubygems_version: 1.8.17
184
+ signing_key:
185
+ specification_version: 3
186
+ summary: Simple
187
+ test_files: []