draftsman 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +3 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +97 -0
- data/LICENSE +20 -0
- data/README.md +506 -0
- data/Rakefile +6 -0
- data/draftsman.gemspec +33 -0
- data/lib/draftsman/config.rb +13 -0
- data/lib/draftsman/draft.rb +289 -0
- data/lib/draftsman/frameworks/cucumber.rb +7 -0
- data/lib/draftsman/frameworks/rails.rb +58 -0
- data/lib/draftsman/frameworks/rspec.rb +16 -0
- data/lib/draftsman/frameworks/sinatra.rb +31 -0
- data/lib/draftsman/model.rb +428 -0
- data/lib/draftsman/serializers/json.rb +17 -0
- data/lib/draftsman/serializers/yaml.rb +17 -0
- data/lib/draftsman/version.rb +3 -0
- data/lib/draftsman.rb +101 -0
- data/lib/generators/draftsman/install_generator.rb +27 -0
- data/lib/generators/draftsman/templates/add_object_changes_column_to_drafts.rb +9 -0
- data/lib/generators/draftsman/templates/config/initializers/draftsman.rb +11 -0
- data/lib/generators/draftsman/templates/create_drafts.rb +22 -0
- data/spec/controllers/informants_controller_spec.rb +27 -0
- data/spec/controllers/users_controller_spec.rb +23 -0
- data/spec/controllers/whodunnits_controller_spec.rb +24 -0
- data/spec/draftsman_spec.rb +19 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/images/rails.png +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +20 -0
- data/spec/dummy/app/controllers/informants_controller.rb +8 -0
- data/spec/dummy/app/controllers/users_controller.rb +8 -0
- data/spec/dummy/app/controllers/whodunnits_controller.rb +8 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/helpers/messages_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/bastard.rb +3 -0
- data/spec/dummy/app/models/child.rb +4 -0
- data/spec/dummy/app/models/parent.rb +5 -0
- data/spec/dummy/app/models/trashable.rb +3 -0
- data/spec/dummy/app/models/vanilla.rb +3 -0
- data/spec/dummy/app/models/whitelister.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +15 -0
- data/spec/dummy/config/application.rb +37 -0
- data/spec/dummy/config/boot.rb +6 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +32 -0
- data/spec/dummy/config/environments/production.rb +73 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +6 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/migrate/20110208155312_set_up_test_tables.rb +86 -0
- data/spec/dummy/db/schema.rb +106 -0
- data/spec/dummy/db/seeds.rb +7 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/lib/tasks/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/models/child_spec.rb +205 -0
- data/spec/models/draft_spec.rb +297 -0
- data/spec/models/parent_spec.rb +191 -0
- data/spec/models/trashable_spec.rb +164 -0
- data/spec/models/vanilla_spec.rb +201 -0
- data/spec/models/whitelister_spec.rb +262 -0
- data/spec/spec_helper.rb +52 -0
- metadata +304 -0
data/draftsman.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
|
2
|
+
require 'draftsman/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'draftsman'
|
6
|
+
s.version = Draftsman::VERSION
|
7
|
+
s.summary = "Create draft versions of your ActiveRecord models' data."
|
8
|
+
s.description = s.summary
|
9
|
+
s.homepage = 'https://github.com/minimalorange/draftsman'
|
10
|
+
s.authors = ['Chris Peters']
|
11
|
+
s.email = 'chris@minimalorange.com'
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
|
+
s.require_paths = ['lib']
|
17
|
+
|
18
|
+
s.add_dependency 'activerecord', ['>= 3.0', '< 5.0']
|
19
|
+
|
20
|
+
s.add_development_dependency 'capybara'
|
21
|
+
s.add_development_dependency 'rake'
|
22
|
+
s.add_development_dependency 'railties', ['>= 3.0', '< 5.0']
|
23
|
+
s.add_development_dependency 'sinatra', '~> 1.0'
|
24
|
+
s.add_development_dependency 'rspec-rails'
|
25
|
+
s.add_development_dependency 'shoulda-matchers'
|
26
|
+
|
27
|
+
# JRuby support for the test ENV
|
28
|
+
if defined?(JRUBY_VERSION)
|
29
|
+
s.add_development_dependency 'activerecord-jdbcsqlite3-adapter', ['>= 1.3.0.rc1', '< 1.4']
|
30
|
+
else
|
31
|
+
s.add_development_dependency 'sqlite3', '~> 1.2'
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,289 @@
|
|
1
|
+
class Draftsman::Draft < ActiveRecord::Base
|
2
|
+
# Mass assignment (for <= ActiveRecord 3.x)
|
3
|
+
if Draftsman.active_record_protected_attributes?
|
4
|
+
attr_accessible :item_type, :item_id, :event, :whodunnit, :object, :object_changes
|
5
|
+
end
|
6
|
+
|
7
|
+
# Associations
|
8
|
+
belongs_to :item, :polymorphic => true
|
9
|
+
|
10
|
+
# Validations
|
11
|
+
validates_presence_of :event
|
12
|
+
|
13
|
+
def self.with_item_keys(item_type, item_id)
|
14
|
+
scoped :conditions => { :item_type => item_type, :item_id => item_id }
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.creates
|
18
|
+
where :event => 'create'
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.destroys
|
22
|
+
where :event => 'destroy'
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.updates
|
26
|
+
where :event => 'update'
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns what changed in this draft. Similar to `ActiveModel::Dirty#changes`.
|
30
|
+
# Returns `nil` if your `drafts` table does not have an `object_changes` text column.
|
31
|
+
def changeset
|
32
|
+
return nil unless self.class.column_names.include? 'object_changes'
|
33
|
+
|
34
|
+
HashWithIndifferentAccess.new(Draftsman.serializer.load(object_changes)).tap do |changes|
|
35
|
+
item_type.constantize.unserialize_draft_attribute_changes(changes)
|
36
|
+
end
|
37
|
+
rescue
|
38
|
+
{}
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns whether or not this is a `create` event.
|
42
|
+
def create?
|
43
|
+
self.event == 'create'
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns whether or not this is a `destroy` event.
|
47
|
+
def destroy?
|
48
|
+
self.event == 'destroy'
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns related draft dependencies that would be along for the ride for a `publish!` action.
|
52
|
+
def draft_publication_dependencies
|
53
|
+
dependencies = []
|
54
|
+
|
55
|
+
case self.event
|
56
|
+
when 'create', 'update'
|
57
|
+
associations = self.item.class.reflect_on_all_associations(:belongs_to)
|
58
|
+
|
59
|
+
associations.each do |association|
|
60
|
+
association_class =
|
61
|
+
if association.polymorphic?
|
62
|
+
self.item.send(association.foreign_key.sub('_id', '_type')).constantize
|
63
|
+
else
|
64
|
+
association.klass
|
65
|
+
end
|
66
|
+
|
67
|
+
if association_class.draftable? && association.name != association_class.draft_association_name.to_sym
|
68
|
+
dependency = self.item.send(association.name)
|
69
|
+
dependencies << dependency.draft if dependency.present? && dependency.draft? && dependency.draft.create?
|
70
|
+
end
|
71
|
+
end
|
72
|
+
when 'destroy'
|
73
|
+
associations = self.item.class.reflect_on_all_associations(:has_one) + self.item.class.reflect_on_all_associations(:has_many)
|
74
|
+
|
75
|
+
associations.each do |association|
|
76
|
+
if association.klass.draftable?
|
77
|
+
# Reconcile different association types into an array, even if `has_one` produces a single-item
|
78
|
+
associated_dependencies =
|
79
|
+
case association.macro
|
80
|
+
when :has_one
|
81
|
+
self.item.send(association.name).present? ? [self.item.send(association.name)] : []
|
82
|
+
when :has_many
|
83
|
+
self.item.send(association.name)
|
84
|
+
end
|
85
|
+
|
86
|
+
associated_dependencies.each do |dependency|
|
87
|
+
dependencies << dependency.draft if dependency.draft?
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
dependencies
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns related draft dependencies that would be along for the ride for a `revert!` action.
|
97
|
+
def draft_reversion_dependencies
|
98
|
+
dependencies = []
|
99
|
+
|
100
|
+
case self.event
|
101
|
+
when 'create'
|
102
|
+
associations = self.item.class.reflect_on_all_associations(:has_one) + self.item.class.reflect_on_all_associations(:has_many)
|
103
|
+
|
104
|
+
associations.each do |association|
|
105
|
+
if association.klass.draftable?
|
106
|
+
# Reconcile different association types into an array, even if `has_one` produces a single-item
|
107
|
+
associated_dependencies =
|
108
|
+
case association.macro
|
109
|
+
when :has_one
|
110
|
+
self.item.send(association.name).present? ? [self.item.send(association.name)] : []
|
111
|
+
when :has_many
|
112
|
+
self.item.send(association.name)
|
113
|
+
end
|
114
|
+
|
115
|
+
associated_dependencies.each do |dependency|
|
116
|
+
dependencies << dependency.draft if dependency.draft?
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
when 'destroy'
|
121
|
+
associations = self.item.class.reflect_on_all_associations(:belongs_to)
|
122
|
+
|
123
|
+
associations.each do |association|
|
124
|
+
association_class =
|
125
|
+
if association.polymorphic?
|
126
|
+
self.item.send(association.foreign_key.sub('_id', '_type')).constantize
|
127
|
+
else
|
128
|
+
association.klass
|
129
|
+
end
|
130
|
+
|
131
|
+
if association_class.draftable? && association_class.trashable? && association.name != association_class.draft_association_name.to_sym
|
132
|
+
dependency = self.item.send(association.name)
|
133
|
+
dependencies << dependency.draft if dependency.present? && dependency.draft? && dependency.draft.destroy?
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
dependencies
|
139
|
+
end
|
140
|
+
|
141
|
+
# Publishes this draft's associated `item`, publishes its `item`'s dependencies, and destroys itself.
|
142
|
+
# - For `create` drafts, adds a value for the `published_at` timestamp on the item and destroys the draft.
|
143
|
+
# - For `update` drafts, applies the drafted changes to the item and destroys the draft.
|
144
|
+
# - For `destroy` drafts, destroys the item and the draft.
|
145
|
+
def publish!
|
146
|
+
ActiveRecord::Base.transaction do
|
147
|
+
case self.event
|
148
|
+
when 'create', 'update'
|
149
|
+
# Parents must be published too
|
150
|
+
self.draft_publication_dependencies.each { |dependency| dependency.publish! }
|
151
|
+
|
152
|
+
# Update drafts need to copy over data to main record
|
153
|
+
self.item.attributes = self.reify.attributes if self.update?
|
154
|
+
|
155
|
+
# Write `published_at` attribute
|
156
|
+
self.item.send "#{self.item.class.published_at_attribute_name}=", Time.now
|
157
|
+
|
158
|
+
# Clear out draft
|
159
|
+
self.item.send "#{self.item.class.draft_association_name}_id=", nil
|
160
|
+
|
161
|
+
# Determine which columns should be updated
|
162
|
+
only = self.item.class.draftsman_options[:only]
|
163
|
+
ignore = self.item.class.draftsman_options[:ignore]
|
164
|
+
skip = self.item.class.draftsman_options[:skip]
|
165
|
+
attributes_to_change = only.any? ? only : self.item.attribute_names
|
166
|
+
attributes_to_change = attributes_to_change - ignore + ['published_at', "#{self.item.class.draft_association_name}_id"] - skip
|
167
|
+
|
168
|
+
# Save without validations or callbacks
|
169
|
+
self.item.update_columns self.item.attributes.slice(*attributes_to_change)
|
170
|
+
self.item.reload
|
171
|
+
|
172
|
+
# Destroy draft
|
173
|
+
self.destroy
|
174
|
+
when 'destroy'
|
175
|
+
self.item.destroy
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# Returns instance of item restored to its pre-draft state.
|
181
|
+
#
|
182
|
+
# Example usage:
|
183
|
+
#
|
184
|
+
# `@category = @category.reify if @category.draft?`
|
185
|
+
def reify
|
186
|
+
without_identity_map do
|
187
|
+
unless self.object.nil?
|
188
|
+
# This appears to be necessary if for some reason the draft's model hasn't been loaded (such as when done in the console).
|
189
|
+
require self.item_type.underscore
|
190
|
+
|
191
|
+
model = item.reload
|
192
|
+
|
193
|
+
Draftsman.serializer.load(self.object).each do |key, value|
|
194
|
+
# Skip counter_cache columns
|
195
|
+
if model.respond_to?("#{key}=") && !key.end_with?('_count')
|
196
|
+
model.send "#{key}=", value
|
197
|
+
elsif !key.end_with?('_count')
|
198
|
+
logger.warn "Attribute #{key} does not exist on #{item_type} (Draft ID: #{id})."
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
model.send "#{model.class.draft_association_name}=", self
|
203
|
+
model
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# Reverts this draft.
|
209
|
+
# - For create drafts, destroys the draft and the item.
|
210
|
+
# - For update drafts, destyors the draft only.
|
211
|
+
# - For destroy drafts, destroys the draft and undoes the `trashed_at` timestamp on the item. If a draft was drafted
|
212
|
+
# for destroy, restores the draft.
|
213
|
+
def revert!
|
214
|
+
ActiveRecord::Base.transaction do
|
215
|
+
case self.event
|
216
|
+
when 'create'
|
217
|
+
self.item.destroy
|
218
|
+
self.destroy
|
219
|
+
when 'update'
|
220
|
+
self.item.class.where(:id => self.item).update_all("#{self.item.class.draft_association_name}_id".to_sym => nil)
|
221
|
+
self.destroy
|
222
|
+
when 'destroy'
|
223
|
+
# Parents must be restored too
|
224
|
+
self.draft_reversion_dependencies.each { |dependency| dependency.revert! }
|
225
|
+
|
226
|
+
# Restore previous draft if one was stashed away
|
227
|
+
if self.previous_draft.present?
|
228
|
+
prev_draft = reify_previous_draft
|
229
|
+
self.item.class.where(:id => self.item).update_all "#{self.item.class.draft_association_name}_id".to_sym => prev_draft.id,
|
230
|
+
self.item.class.trashed_at_attribute_name => nil
|
231
|
+
else
|
232
|
+
self.item.class.where(:id => self.item).update_all "#{self.item.class.draft_association_name}_id".to_sym => nil,
|
233
|
+
self.item.class.trashed_at_attribute_name => nil
|
234
|
+
end
|
235
|
+
|
236
|
+
self.destroy
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
# Returns whether or not this is an `update` event.
|
242
|
+
def update?
|
243
|
+
self.event == 'update'
|
244
|
+
end
|
245
|
+
|
246
|
+
private
|
247
|
+
|
248
|
+
# Restores previous draft and returns it.
|
249
|
+
def reify_previous_draft
|
250
|
+
draft = self.class.new
|
251
|
+
|
252
|
+
without_identity_map do
|
253
|
+
Draftsman.serializer.load(self.previous_draft).each do |key, value|
|
254
|
+
if key.to_sym != :id && draft.respond_to?("#{key}=")
|
255
|
+
draft.send "#{key}=", value
|
256
|
+
elsif key.to_sym != :id
|
257
|
+
logger.warn "Attribute #{key} does not exist on #{item_type} (Draft ID: #{self.id})."
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
draft.save!
|
263
|
+
draft
|
264
|
+
end
|
265
|
+
|
266
|
+
# Saves associated draft dependencies by reflecting `belongs_to` associations and identifying which ones are
|
267
|
+
# draftable.
|
268
|
+
#def save_draft_dependencies
|
269
|
+
# self.item.class.reflect_on_all_associations(:belongs_to).each do |association|
|
270
|
+
# associated_object = self.item.send(association.name)
|
271
|
+
#
|
272
|
+
# if associated_object.present? && associated_object.respond_to?(:draft?)
|
273
|
+
# if associated_object.reload.draft?
|
274
|
+
# Draftsman::DraftDependency.create(:draft_id => self.id, :dependency_id => associated_object.id)
|
275
|
+
# else
|
276
|
+
# Draftsman::DraftDependency.where(:draft_id => self.id, :dependency_id => associated_object.id).delete_all
|
277
|
+
# end
|
278
|
+
# end
|
279
|
+
# end
|
280
|
+
#end
|
281
|
+
|
282
|
+
def without_identity_map(&block)
|
283
|
+
if defined?(ActiveRecord::IdentityMap) && ActiveRecord::IdentityMap.respond_to?(:without)
|
284
|
+
ActiveRecord::IdentityMap.without &block
|
285
|
+
else
|
286
|
+
block.call
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Draftsman
|
2
|
+
module Rails
|
3
|
+
module Controller
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
if defined?(ActionController) && base == ActionController::Base
|
7
|
+
base.before_filter :set_draftsman_whodunnit, :set_draftsman_controller_info
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
# Returns the user who is responsible for any changes that occur.
|
14
|
+
# By default this calls `current_user` and returns the result.
|
15
|
+
#
|
16
|
+
# Override this method in your controller to call a different
|
17
|
+
# method, e.g. `current_person`, or anything you like.
|
18
|
+
def user_for_draftsman
|
19
|
+
current_user if defined?(current_user)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns any information about the controller or request that you
|
23
|
+
# want Draftsman to store alongside any changes that occur. By
|
24
|
+
# default, this returns an empty hash.
|
25
|
+
#
|
26
|
+
# Override this method in your controller to return a hash of any
|
27
|
+
# information you need. The hash's keys must correspond to columns
|
28
|
+
# in your `drafts` table, so don't forget to add any new columns
|
29
|
+
# you need.
|
30
|
+
#
|
31
|
+
# For example:
|
32
|
+
#
|
33
|
+
# {:ip => request.remote_ip, :user_agent => request.user_agent}
|
34
|
+
#
|
35
|
+
# The columns `ip` and `user_agent` must exist in your `drafts` # table.
|
36
|
+
#
|
37
|
+
# Use the `:meta` option to `Draftsman::Model::ClassMethods.has_drafts`
|
38
|
+
# to store any extra model-level data you need.
|
39
|
+
def info_for_draftsman
|
40
|
+
{}
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# Tells Draftsman who is responsible for any changes that occur.
|
46
|
+
def set_draftsman_whodunnit
|
47
|
+
::Draftsman.whodunnit = user_for_draftsman
|
48
|
+
end
|
49
|
+
|
50
|
+
# Tells Draftsman any information from the controller you want
|
51
|
+
# to store alongside any changes that occur.
|
52
|
+
def set_draftsman_controller_info
|
53
|
+
::Draftsman.controller_info = info_for_draftsman
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
if defined? RSpec
|
2
|
+
require 'rspec/core'
|
3
|
+
require 'rspec/matchers'
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
config.before(:each) do
|
7
|
+
::Draftsman.whodunnit = nil
|
8
|
+
::Draftsman.controller_info = {} if defined?(::Rails) && defined?(::RSpec::Rails)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
RSpec::Matchers.define :be_draftable do
|
13
|
+
# check to see if the model has `has_drafts` declared on it
|
14
|
+
match { |actual| actual.kind_of?(::Draftsman::Model::InstanceMethods) }
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Sinatra
|
2
|
+
module Draftsman
|
3
|
+
|
4
|
+
# Register this module inside your Sinatra application to gain access to controller-level methods used by Draftsman
|
5
|
+
def self.registered(app)
|
6
|
+
app.helpers Sinatra::Draftsman
|
7
|
+
app.before { set_draftsman_whodunnit }
|
8
|
+
end
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
# Returns the user who is responsible for any changes that occur.
|
13
|
+
# By default this calls `current_user` and returns the result.
|
14
|
+
#
|
15
|
+
# Override this method in your controller to call a different
|
16
|
+
# method, e.g. `current_person`, or anything you like.
|
17
|
+
def user_for_draftsman
|
18
|
+
current_user if defined?(current_user)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# Tells Draftsman who is responsible for any changes that occur.
|
24
|
+
def set_draftsman_whodunnit
|
25
|
+
::Draftsman.whodunnit = user_for_draftsman
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
register Sinatra::Draftsman if defined?(register)
|
31
|
+
end
|