draftsman 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c75a20681563bfa82fba2f109838a5fb2688d043
4
- data.tar.gz: 7f43cc039a9d07048fbdfc04cfd9f8c5ff764ce1
3
+ metadata.gz: c32bf1dbb24a4fea24cf03abe3c859a965a46cae
4
+ data.tar.gz: c46acc2bf65572dc84890b66326d98a4d01c8c51
5
5
  SHA512:
6
- metadata.gz: 032096c7295407be9a2ba40e8ccd51e5e595279fef880d0011145867c32ccf3df60e5a47282afeacc7a5b5022745cf0531d96b03cb34a43931577cdcbeabad85
7
- data.tar.gz: 6eb88502e4c4f176cb85660614597eea419425d14a35ff2390ae0f6b9a71718fd7d84adce866f82ab904f605c9c82cd967881d2a037ac932a43376bef539544f
6
+ metadata.gz: d25357aecfb9899638710cebb6950b05f7f1359b8151f62a2965b3eaf358ec71e7ad1bc27a347eba321ce97ea3999803ba387c3bfc817703c5eb8b62de1f24cb
7
+ data.tar.gz: 3c0f135e0d1d5920344e879b8dbb26ede69ae6a6ee8c0dec43d5f5df8a4e6bfab251821b2c9daac3de8b2691f7f529b575a75bfdbe80b5febfec414aaf88a902
data/.gitignore CHANGED
@@ -8,3 +8,4 @@ pkg/*
8
8
  spec/dummy/tmp/
9
9
  spec/dummy/db/*.sqlite3
10
10
  spec/dummy/log/*.log
11
+ Gemfile.lock
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- ruby-2.1.2
1
+ ruby-2.2.1
data/CHANGELOG.md CHANGED
@@ -1,16 +1,22 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 0.3.2 - April 6, 2015
4
+
5
+ - Fixed [#8](https://github.com/liveeditor/draftsman/issues/8) - Update specs to use new community standards
6
+ - Fixed [#9](https://github.com/liveeditor/draftsman/issues/9) - Sinatra extension should not use Sinatra base namespace
7
+ - Fixed [#12](https://github.com/liveeditor/draftsman/issues/12) - JSON::ParserError when draft_destroying a widget which was just created
8
+
3
9
  ## 0.3.1 - August 14, 2014
4
10
 
5
- - Commit [aae737fcdf](https://github.com/live-editor/draftsman/commit/aae737fcdf48604bc480b1c9c141bf642c0f581c) - `skip` option not persisting skipped values correctly
11
+ - Commit [aae737f](https://github.com/live-editor/draftsman/commit/aae737fcdf48604bc480b1c9c141bf642c0f581c) - `skip` option not persisting skipped values correctly
6
12
 
7
13
  ## 0.3.0 - July 29, 2014
8
14
 
9
- - Commit [1e2a59f678](https://github.com/live-editor/draftsman/commit/1e2a59f678cc4d88222dfc1976d564b5649cd329) - Add support for PostgreSQL JSON data type for `object`, `object_changes`, and `previous_draft` columns.
15
+ - Commit [1e2a59f](https://github.com/live-editor/draftsman/commit/1e2a59f678cc4d88222dfc1976d564b5649cd329) - Add support for PostgreSQL JSON data type for `object`, `object_changes`, and `previous_draft` columns.
10
16
 
11
17
  ## v0.2.1 - June 28, 2014
12
18
 
13
- - Commit [dbc6c83abb](https://github.com/live-editor/draftsman/commit/dbc6c83abbea5211f67ad883f4a2d18a9f5ac181) - Reifying a record that was drafted for destruction uses data from a drafted update before that if that's what happened.
19
+ - Commit [dbc6c83](https://github.com/live-editor/draftsman/commit/dbc6c83abbea5211f67ad883f4a2d18a9f5ac181) - Reifying a record that was drafted for destruction uses data from a drafted update before that if that's what happened.
14
20
 
15
21
  ## v0.2.0 - June 3, 2014
16
22
 
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Draftsman v0.3.1 (alpha)
1
+ # Draftsman v0.3.2 (alpha)
2
2
 
3
3
  Draftsman is a Ruby gem that lets you create draft versions of your database records. If you're developing a system in
4
4
  need of simple drafts or a publishing approval queue, then Draftsman just might be what you need.
data/bin/bundler ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'bundler' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('bundler', 'bundler')
data/bin/erubis ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'erubis' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('draftsman', 'erubis')
data/bin/htmldiff ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'htmldiff' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('draftsman', 'htmldiff')
data/bin/ldiff ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'ldiff' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('draftsman', 'ldiff')
data/bin/nokogiri ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'nokogiri' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('draftsman', 'nokogiri')
data/bin/rackup ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rackup' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('draftsman', 'rackup')
data/bin/rails ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rails' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('railties', 'rails')
data/bin/rake ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rake' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('draftsman', 'rake')
data/bin/rspec ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rspec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rspec-core', 'rspec')
data/bin/thor ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'thor' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('thor', 'thor')
data/bin/tilt ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'tilt' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('tilt', 'tilt')
data/draftsman.gemspec CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |s|
6
6
  s.version = Draftsman::VERSION
7
7
  s.summary = "Create draft versions of your ActiveRecord models' data. Works with Ruby on Rails and Sinatra."
8
8
  s.description = s.summary
9
- s.homepage = 'https://github.com/live-editor/draftsman'
9
+ s.homepage = 'https://github.com/liveeditor/draftsman'
10
10
  s.authors = ['Chris Peters']
11
11
  s.email = 'chris@minimalorange.com'
12
12
  s.license = 'MIT'
@@ -18,12 +18,10 @@ Gem::Specification.new do |s|
18
18
 
19
19
  s.add_dependency 'activerecord', ['>= 3.0', '< 5.0']
20
20
 
21
- s.add_development_dependency 'capybara'
22
21
  s.add_development_dependency 'rake'
23
22
  s.add_development_dependency 'railties', ['>= 3.0', '< 5.0']
24
23
  s.add_development_dependency 'sinatra', '~> 1.0'
25
- s.add_development_dependency 'rspec-rails'
26
- s.add_development_dependency 'shoulda-matchers'
24
+ s.add_development_dependency 'rspec-rails', '3.2.1'
27
25
 
28
26
  # JRuby support for the test ENV
29
27
  if defined?(JRUBY_VERSION)
@@ -1,7 +1,7 @@
1
1
  class Draftsman::Draft < ActiveRecord::Base
2
2
  # Mass assignment (for <= ActiveRecord 3.x)
3
3
  if Draftsman.active_record_protected_attributes?
4
- attr_accessible :item_type, :item_id, :event, :whodunnit, :object, :object_changes
4
+ attr_accessible :item_type, :item_id, :item, :event, :whodunnit, :object, :object_changes, :previous_draft
5
5
  end
6
6
 
7
7
  # Associations
@@ -32,6 +32,11 @@ class Draftsman::Draft < ActiveRecord::Base
32
32
  @object_changes_col_is_json ||= columns_hash['object_changes'].type == :json
33
33
  end
34
34
 
35
+ # Returns whether the `previous_draft` column is using the `json` type supported by PostgreSQL.
36
+ def self.previous_changes_col_is_json?
37
+ @previous_changes_col_is_json ||= columns_hash['object_changes'].type == :json
38
+ end
39
+
35
40
  def self.updates
36
41
  where :event => 'update'
37
42
  end
@@ -72,7 +77,7 @@ class Draftsman::Draft < ActiveRecord::Base
72
77
 
73
78
  associations.each do |association|
74
79
  association_class =
75
- if association.polymorphic?
80
+ if association.options.key?(:polymorphic)
76
81
  my_item.send(association.foreign_key.sub('_id', '_type')).constantize
77
82
  else
78
83
  association.klass
@@ -136,7 +141,7 @@ class Draftsman::Draft < ActiveRecord::Base
136
141
 
137
142
  associations.each do |association|
138
143
  association_class =
139
- if association.polymorphic?
144
+ if association.options.key?(:polymorphic)
140
145
  self.item.send(association.foreign_key.sub('_id', '_type')).constantize
141
146
  else
142
147
  association.klass
@@ -180,7 +185,11 @@ class Draftsman::Draft < ActiveRecord::Base
180
185
  attributes_to_change = attributes_to_change - ignore + ['published_at', "#{self.item.class.draft_association_name}_id"] - skip
181
186
 
182
187
  # Save without validations or callbacks
183
- self.item.update_columns self.item.attributes.slice(*attributes_to_change)
188
+ self.item.attributes.slice(*attributes_to_change).each do |key, value|
189
+ self.item.send("#{key}=", value)
190
+ end
191
+ self.item.save(:validate => false)
192
+
184
193
  self.item.reload
185
194
 
186
195
  # Destroy draft
@@ -1,9 +1,11 @@
1
- module Sinatra
2
- module Draftsman
1
+ require 'active_support/core_ext/object' # provides the `try` method
2
+
3
+ module Draftsman
4
+ module Sinatra
3
5
 
4
6
  # Register this module inside your Sinatra application to gain access to controller-level methods used by Draftsman
5
7
  def self.registered(app)
6
- app.helpers Sinatra::Draftsman
8
+ app.helpers self
7
9
  app.before { set_draftsman_whodunnit }
8
10
  end
9
11
 
@@ -15,17 +17,20 @@ module Sinatra
15
17
  # Override this method in your controller to call a different
16
18
  # method, e.g. `current_person`, or anything you like.
17
19
  def user_for_draftsman
18
- current_user if defined?(current_user)
20
+ return unless defined?(current_user)
21
+ ActiveSupport::VERSION::MAJOR >= 4 ? current_user.try!(:id) : current_user.try(:id)
22
+ rescue NoMethodError
23
+ current_user
19
24
  end
20
25
 
21
26
  private
22
27
 
23
28
  # Tells Draftsman who is responsible for any changes that occur.
24
29
  def set_draftsman_whodunnit
25
- ::Draftsman.whodunnit = user_for_draftsman
30
+ ::Draftsman.whodunnit = user_for_draftsman if ::Draftsman.enabled?
26
31
  end
27
32
 
28
33
  end
29
34
 
30
- register Sinatra::Draftsman if defined?(register)
35
+ ::Sinatra.register Draftsman::Sinatra if defined?(::Sinatra)
31
36
  end
@@ -120,11 +120,6 @@ module Draftsman
120
120
  method_defined?(:draftsman_options)
121
121
  end
122
122
 
123
- # Returns whether or not the included ActiveRecord can do `where.not(...)` style queries.
124
- def where_not?
125
- ActiveRecord::VERSION::STRING.to_f >= 4.0
126
- end
127
-
128
123
  # Serializes attribute changes for `Draft#object_changes` attribute.
129
124
  def serialize_draft_attribute_changes(changes)
130
125
  # Don't serialize values before inserting into columns of type `JSON` on PostgreSQL databases.
@@ -183,6 +178,11 @@ module Draftsman
183
178
  end
184
179
  end
185
180
  end
181
+
182
+ # Returns whether or not the included ActiveRecord can do `where.not(...)` style queries.
183
+ def where_not?
184
+ ActiveRecord::VERSION::STRING.to_f >= 4.0
185
+ end
186
186
  end
187
187
 
188
188
  module InstanceMethods
@@ -231,7 +231,13 @@ module Draftsman
231
231
 
232
232
  # Stash previous draft in case it needs to be reverted later
233
233
  if self.draft?
234
- data[:previous_draft] = Draftsman.serializer.dump(send(self.class.draft_association_name).attributes)
234
+ attrs = send(self.class.draft_association_name).attributes
235
+
236
+ data[:previous_draft] = if self.class.draft_class.previous_changes_col_is_json?
237
+ attrs
238
+ else
239
+ Draftsman.serializer.dump(attrs)
240
+ end
235
241
  end
236
242
 
237
243
  data = merge_metadata_for_draft(data)
@@ -1,3 +1,3 @@
1
1
  module Draftsman
2
- VERSION = '0.3.1'
2
+ VERSION = '0.3.2'
3
3
  end
@@ -1,27 +1,45 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  # Tests controller `info_for_draftsman` method
4
- describe InformantsController do
4
+ describe InformantsController, :type => :controller do
5
5
  let(:trashable) { Trashable.create!(:name => 'Bob') }
6
6
 
7
- describe :create do
7
+ describe 'create' do
8
8
  before { post :create }
9
9
  subject { Draftsman::Draft.last }
10
- its(:ip) { should eql '123.45.67.89' }
11
- its(:user_agent) { should eql '007' }
10
+
11
+ it 'records `ip` from custom `info_for_draftsman`' do
12
+ expect(subject.ip).to eql '123.45.67.89'
13
+ end
14
+
15
+ it 'records `user_agent` from custom `info_for_draftsman`' do
16
+ expect(subject.user_agent).to eql '007'
17
+ end
12
18
  end
13
19
 
14
- describe :update do
20
+ describe 'update' do
15
21
  before { put :update, :id => trashable.id }
16
22
  subject { Draftsman::Draft.last }
17
- its(:ip) { should eql '123.45.67.89' }
18
- its(:user_agent) { should eql '007' }
23
+
24
+ it 'records `ip` from custom `info_for_draftsman`' do
25
+ expect(subject.ip).to eql '123.45.67.89'
26
+ end
27
+
28
+ it 'records `user_agent` from custom `info_for_draftsman`' do
29
+ expect(subject.user_agent).to eql '007'
30
+ end
19
31
  end
20
32
 
21
- describe :destroy do
33
+ describe 'destroy' do
22
34
  before { delete :destroy, :id => trashable.id }
23
35
  subject { Draftsman::Draft.last }
24
- its(:ip) { should eql '123.45.67.89' }
25
- its(:user_agent) { should eql '007' }
36
+
37
+ it 'records `ip` from custom `info_for_draftsman`' do
38
+ expect(subject.ip).to eql '123.45.67.89'
39
+ end
40
+
41
+ it 'records `user_agent` from custom `info_for_draftsman`' do
42
+ expect(subject.user_agent).to eql '007'
43
+ end
26
44
  end
27
45
  end
@@ -1,23 +1,32 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe UsersController do
3
+ describe UsersController, :type => :controller do
4
4
  let(:trashable) { Trashable.create!(:name => 'Bob') }
5
5
 
6
- describe :create do
6
+ describe 'create' do
7
7
  before { post :create }
8
8
  subject { Draftsman::Draft.last }
9
- its(:whodunnit) { should eql 'A User' }
9
+
10
+ it 'records user name via `user_for_draftsman`' do
11
+ expect(subject.whodunnit).to eql 'A User'
12
+ end
10
13
  end
11
14
 
12
- describe :update do
15
+ describe 'update' do
13
16
  before { put :update, :id => trashable.id }
14
17
  subject { return Draftsman::Draft.last }
15
- its(:whodunnit) { should eql 'A User' }
18
+
19
+ it 'records user name via `user_for_draftsman`' do
20
+ expect(subject.whodunnit).to eql 'A User'
21
+ end
16
22
  end
17
23
 
18
- describe :destroy do
24
+ describe 'destroy' do
19
25
  before { delete :destroy, :id => trashable.id }
20
26
  subject { return Draftsman::Draft.last }
21
- its(:whodunnit) { should eql 'A User' }
27
+
28
+ it 'records user name via `user_for_draftsman`' do
29
+ expect(subject.whodunnit).to eql 'A User'
30
+ end
22
31
  end
23
32
  end
@@ -1,24 +1,33 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  # Tests the automatic usage of `current_user` as the `whodunnit` attribute on the draft object
4
- describe WhodunnitsController do
4
+ describe WhodunnitsController, :type => :controller do
5
5
  let(:trashable) { Trashable.create!(:name => 'Bob') }
6
6
 
7
- describe :create do
7
+ describe 'create' do
8
8
  before { post :create }
9
9
  subject { Draftsman::Draft.last }
10
- its(:whodunnit) { should eql "153" }
10
+
11
+ it 'records `current_user` via `user_for_draftsman' do
12
+ expect(subject.whodunnit).to eql "153"
13
+ end
11
14
  end
12
15
 
13
- describe :update do
16
+ describe 'update' do
14
17
  before { put :update, :id => trashable.id }
15
18
  subject { Draftsman::Draft.last }
16
- its(:whodunnit) { should eql "153" }
19
+
20
+ it 'records `current_user` via `user_for_draftsman' do
21
+ expect(subject.whodunnit).to eql "153"
22
+ end
17
23
  end
18
24
 
19
- describe :destroy do
25
+ describe 'destroy' do
20
26
  before { delete :destroy, :id => trashable.id }
21
27
  subject { Draftsman::Draft.last }
22
- its(:whodunnit) { should eql "153" }
28
+
29
+ it 'records `current_user` via `user_for_draftsman' do
30
+ expect(subject.whodunnit).to eql "153"
31
+ end
23
32
  end
24
33
  end
@@ -1,19 +1,25 @@
1
1
  require 'spec_helper.rb'
2
2
 
3
3
  describe ::Draftsman do
4
- describe 'Sanity Test' do
5
- it { should be_a Module }
4
+ subject { ::Draftsman }
5
+
6
+ it 'passes our sanity test' do
7
+ expect(subject).to be_a Module
6
8
  end
7
9
 
8
10
  describe :whodunnit do
9
11
  before(:all) { ::Draftsman.whodunnit = 'foobar' }
10
- # Clears out `source` before each test
11
- its(:whodunnit) { should be_nil }
12
+
13
+ it 'clears out `whodunnit` before each test' do
14
+ expect(subject.whodunnit).to be_nil
15
+ end
12
16
  end
13
17
 
14
18
  describe :controller_info do
15
19
  before(:all) { ::Draftsman.controller_info = { foo: 'bar' } }
16
- # Clears out `controller_info` before each test
17
- its(:controller_info) { should == {} }
20
+
21
+ it 'clears out `controller_info` before each test' do
22
+ expect(subject.controller_info).to eql Hash.new
23
+ end
18
24
  end
19
25
  end