socialization 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -0
  3. data/.travis.yml +2 -1
  4. data/Appraisals +13 -1
  5. data/CHANGELOG.md +12 -0
  6. data/Guardfile +12 -0
  7. data/LICENSE +2 -2
  8. data/README.md +46 -1
  9. data/Rakefile +8 -10
  10. data/lib/socialization/stores/active_record/follow.rb +4 -0
  11. data/lib/socialization/stores/active_record/like.rb +4 -0
  12. data/lib/socialization/stores/active_record/mention.rb +4 -0
  13. data/lib/socialization/stores/active_record/mixins/base.rb +4 -0
  14. data/lib/socialization/stores/mixins/follow.rb +1 -1
  15. data/lib/socialization/version.rb +1 -1
  16. data/socialization.gemspec +5 -3
  17. data/spec/actors/follower_spec.rb +113 -0
  18. data/spec/actors/liker_spec.rb +105 -0
  19. data/spec/actors/mentioner_spec.rb +105 -0
  20. data/spec/spec_helper.rb +21 -0
  21. data/{test/test_helper.rb → spec/spec_support/data_stores.rb} +76 -81
  22. data/spec/spec_support/matchers.rb +54 -0
  23. data/spec/stores/active_record/follow_store_spec.rb +147 -0
  24. data/spec/stores/active_record/like_store_spec.rb +146 -0
  25. data/spec/stores/active_record/mention_store_spec.rb +148 -0
  26. data/spec/stores/active_record/mixins/base_spec.rb +25 -0
  27. data/spec/stores/redis/base_spec.rb +195 -0
  28. data/spec/stores/redis/config_spec.rb +28 -0
  29. data/spec/stores/redis/follow_store_spec.rb +26 -0
  30. data/spec/stores/redis/like_store_spec.rb +23 -0
  31. data/spec/stores/redis/mention_store_spec.rb +23 -0
  32. data/spec/string_spec.rb +13 -0
  33. data/spec/victims/followable_spec.rb +59 -0
  34. data/spec/victims/likeable_spec.rb +58 -0
  35. data/spec/victims/mentionable_spec.rb +59 -0
  36. data/spec/world_spec.rb +107 -0
  37. metadata +79 -42
  38. data/test/actors/follower_test.rb +0 -114
  39. data/test/actors/liker_test.rb +0 -106
  40. data/test/actors/mentioner_test.rb +0 -106
  41. data/test/stores/active_record/follow_store_test.rb +0 -138
  42. data/test/stores/active_record/like_store_test.rb +0 -139
  43. data/test/stores/active_record/mention_store_test.rb +0 -138
  44. data/test/stores/redis/base_test.rb +0 -203
  45. data/test/stores/redis/config_test.rb +0 -30
  46. data/test/stores/redis/follow_store_test.rb +0 -27
  47. data/test/stores/redis/like_store_test.rb +0 -25
  48. data/test/stores/redis/mention_store_test.rb +0 -25
  49. data/test/string_test.rb +0 -13
  50. data/test/victims/followable_test.rb +0 -60
  51. data/test/victims/likeable_test.rb +0 -60
  52. data/test/victims/mentionable_test.rb +0 -60
  53. data/test/world_test.rb +0 -112
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 39b3c656c408687fa0ee59c6290a726c06cc99d9
4
- data.tar.gz: 04842d1fba2aefd0d43a4196493cfd466de10f6a
3
+ metadata.gz: a760308b08f93a8929499b6ba5b1c3d9ddf66b19
4
+ data.tar.gz: e5c2ec3cd08861113267525060542682da32eaa7
5
5
  SHA512:
6
- metadata.gz: a0d79112e37012c0efe43a47f75a03b6b5a727b79c22ab70bbdb89d0d37bc55ca55e932f7c3552043eb33080d1fa0e5fb5f0c7fb74393966524dd7b172804ffd
7
- data.tar.gz: d63f0418b794e48c4bed540fa096cfe886ba85f3db61fe7eb5eceadd03536a2ef422e722fa2f62774dc56049df99da97febad6c4529541d7eafd5ed7bd49b54f
6
+ metadata.gz: f935cb82ad88a7db6c2a09d6d3232e8be9ff276a2b70b2a38cfe5a12941081f265eb0920a78ece38c34deae21632f84e27dd94fe8d4223aaf8b654952c816e5f
7
+ data.tar.gz: 4207ebfefb3b53e477a23ff830071708623fd3a540eb26994eee18514ec432890c590bc9cbf02e8dc9289efc334c9d6c63c9c9546ecc23ccb4901c96d3c852c9
@@ -0,0 +1 @@
1
+ 2.2.2
@@ -3,5 +3,6 @@ rvm:
3
3
  - 1.9.3
4
4
  - 2.0.0
5
5
  - 2.1.0
6
+ - 2.2.2
6
7
 
7
- script: bundle exec rake test
8
+ script: bundle exec rake spec
data/Appraisals CHANGED
@@ -8,4 +8,16 @@ end
8
8
 
9
9
  appraise "activerecord32" do
10
10
  gem "activerecord", "~> 3.2.1"
11
- end
11
+ end
12
+
13
+ appraise "activerecord40" do
14
+ gem "activerecord", "~> 4.0.13"
15
+ end
16
+
17
+ appraise "activerecord41" do
18
+ gem "activerecord", "~> 4.1.12"
19
+ end
20
+
21
+ appraise "activerecord42" do
22
+ gem "activerecord", "~> 4.2.3"
23
+ end
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.2 (August 8, 2015)
4
+
5
+ * Support for ActiveRecord counter caches (thanks @samnang)
6
+ * Moved entire test suite to RSpec
7
+
8
+
3
9
  ## 1.0.1 (January 8, 2014)
4
10
 
5
11
  * Supports Rails 4 (thanks @thomas88)
@@ -10,6 +16,7 @@
10
16
 
11
17
  * Same as 0.5.0.beta4. Been using it in a very high traffic production environment for over a year and it works.
12
18
 
19
+
13
20
  ## 0.5.0.beta (June 6, 2012)
14
21
 
15
22
  * **IMPORTANT:** This release includes many changes, some breaking. Make sure to test your code carefully after upgrading.
@@ -24,11 +31,13 @@
24
31
  * Added: support for single table inheritance. Thanks to [@balvig](https://github.com/balvig).
25
32
  * Changed: raises Socialization::ArgumentError instead of ::ArgumentError
26
33
 
34
+
27
35
  ## v0.4.0 (February 25, 2012)
28
36
 
29
37
  * **BREAKING CHANGE:** Renamed `mentionner` to `mentioner`. This is proper English.
30
38
  * Added: `followees`, `likees` and `mentionees` methods to `Follower`, `Liker` and `Mentioner`. Thanks to [@ihara2525](https://github.com/ihara2525).
31
39
 
40
+
32
41
  ## v0.3.0 (February 22, 2012)
33
42
 
34
43
  * **BREAKING CHANGE:** `likers`, `followers` now return a scope instead of an array. They also require to have the class of the desired scope as an argument. For example: `Movie.find(1).followers(User)`.
@@ -41,15 +50,18 @@
41
50
  * Improved tests.
42
51
  * Changed: Can no longer like or follow yourself.
43
52
 
53
+
44
54
  ## 0.2.1 (January 15, 2012)
45
55
 
46
56
  * Bug fixes
47
57
 
58
+
48
59
  ## 0.2.0 (January 15, 2012)
49
60
 
50
61
  * Bug fixes
51
62
  * Made Ruby 1.8.7 compatible
52
63
 
64
+
53
65
  ## 0.1.0 (January 14, 2012)
54
66
 
55
67
  * Initial release
@@ -0,0 +1,12 @@
1
+ guard :rspec, cmd: "bundle exec rspec" do
2
+ require "guard/rspec/dsl"
3
+ dsl = Guard::RSpec::Dsl.new(self)
4
+
5
+ rspec = dsl.rspec
6
+ watch(rspec.spec_helper) { rspec.spec_dir }
7
+ watch(rspec.spec_support) { rspec.spec_dir }
8
+ watch(%r{^spec/.+_spec\.rb$})
9
+
10
+ ruby = dsl.ruby
11
+ dsl.watch_spec_files_for(ruby.lib_files)
12
+ end
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012 Carl Mercier
1
+ Copyright (c) 2015 Carl Mercier
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
17
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
18
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
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.
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -119,6 +119,13 @@ What items are you following (given that an Item model is followed)?
119
119
 
120
120
  user.followees(Item)
121
121
 
122
+ Number of followees (Requires followees_count column in db)
123
+
124
+ def change
125
+ add_column :#{Table_name}, :followees_count, :integer, :default => 0
126
+ end
127
+
128
+ user.followees_count
122
129
 
123
130
  ***
124
131
 
@@ -133,6 +140,13 @@ All followers
133
140
 
134
141
  celebrity.followers(User)
135
142
 
143
+ Number of followers (Requires followers_count column in db)
144
+
145
+ def change
146
+ add_column :#{Table_name}, :followers_count, :integer, :default => 0
147
+ end
148
+
149
+ celebrity.followers_count
136
150
 
137
151
  ***
138
152
 
@@ -155,6 +169,14 @@ Likes?
155
169
 
156
170
  user.likes?(movie)
157
171
 
172
+ Number of likees (Requires likees_count column in db)
173
+
174
+ def change
175
+ add_column :#{Table_name}, :likees_count, :integer, :default => 0
176
+ end
177
+
178
+ user.likees_count
179
+
158
180
  ***
159
181
 
160
182
 
@@ -168,6 +190,14 @@ All likers
168
190
 
169
191
  movie.likers(User)
170
192
 
193
+ Number of likers (Requires likers_count column in db)
194
+
195
+ def change
196
+ add_column :#{Table_name}, :likers_count, :integer, :default => 0
197
+ end
198
+
199
+ movie.likers_count
200
+
171
201
  ***
172
202
 
173
203
 
@@ -195,6 +225,14 @@ All mentionees
195
225
 
196
226
  comment.mentionees(User)
197
227
 
228
+ Number of mentionees (Requires mentionees column in db)
229
+
230
+ def change
231
+ add_column :#{Table_name}, : mentionees, :integer, :default => 0
232
+ end
233
+
234
+ user. mentionees_count
235
+
198
236
  ***
199
237
 
200
238
 
@@ -208,6 +246,13 @@ All mentioners
208
246
 
209
247
  user.mentioners(Comment)
210
248
 
249
+ Number of mentioners (Requires mentioners_count column in db)
250
+
251
+ def change
252
+ add_column :#{Table_name}, :mentioners_count, :integer, :default => 0
253
+ end
254
+
255
+ movie.mentioners_count
211
256
 
212
257
  ***
213
258
 
@@ -250,4 +295,4 @@ To use the demo app:
250
295
 
251
296
  ## Copyright
252
297
 
253
- Copyright (c) 2012-2014 Carl Mercier -- Released under the MIT license.
298
+ Copyright (c) 2012-2015 Carl Mercier -- Released under the MIT license.
data/Rakefile CHANGED
@@ -1,16 +1,14 @@
1
1
  require 'rake'
2
- require 'rake/testtask'
3
2
  require 'bundler/gem_tasks'
4
3
  require 'appraisal'
5
4
 
6
- desc 'Default: run unit tests against all supported versions of ActiveRecord'
7
- task :default => ["appraisal:install"] do |t|
8
- exec("rake appraisal test")
9
- end
5
+ begin
6
+ require 'rspec/core/rake_task'
7
+ RSpec::Core::RakeTask.new(:spec)
10
8
 
11
- desc 'Test the socialization plugin.'
12
- Rake::TestTask.new(:test) do |t|
13
- t.libs << 'lib'
14
- t.pattern = 'test/**/*_test.rb'
15
- t.verbose = true
9
+ desc 'Default: run unit tests against all supported versions of ActiveRecord'
10
+ task :default => ["appraisal:install"] do |t|
11
+ exec("rake appraisal spec")
12
+ end
13
+ rescue LoadError
16
14
  end
@@ -25,6 +25,8 @@ module Socialization
25
25
  follow.follower = follower
26
26
  follow.followable = followable
27
27
  end
28
+ update_counter(follower, followees_count: +1)
29
+ update_counter(followable, followers_count: +1)
28
30
  call_after_create_hooks(follower, followable)
29
31
  true
30
32
  else
@@ -35,6 +37,8 @@ module Socialization
35
37
  def unfollow!(follower, followable)
36
38
  if follows?(follower, followable)
37
39
  follow_for(follower, followable).destroy_all
40
+ update_counter(follower, followees_count: -1)
41
+ update_counter(followable, followers_count: -1)
38
42
  call_after_destroy_hooks(follower, followable)
39
43
  true
40
44
  else
@@ -25,6 +25,8 @@ module Socialization
25
25
  like.liker = liker
26
26
  like.likeable = likeable
27
27
  end
28
+ update_counter(liker, likees_count: +1)
29
+ update_counter(likeable, likers_count: +1)
28
30
  call_after_create_hooks(liker, likeable)
29
31
  true
30
32
  else
@@ -35,6 +37,8 @@ module Socialization
35
37
  def unlike!(liker, likeable)
36
38
  if likes?(liker, likeable)
37
39
  like_for(liker, likeable).destroy_all
40
+ update_counter(liker, likees_count: -1)
41
+ update_counter(likeable, likers_count: -1)
38
42
  call_after_destroy_hooks(liker, likeable)
39
43
  true
40
44
  else
@@ -25,6 +25,8 @@ module Socialization
25
25
  mention.mentioner = mentioner
26
26
  mention.mentionable = mentionable
27
27
  end
28
+ update_counter(mentioner, mentionees_count: +1)
29
+ update_counter(mentionable, mentioners_count: +1)
28
30
  call_after_create_hooks(mentioner, mentionable)
29
31
  true
30
32
  else
@@ -35,6 +37,8 @@ module Socialization
35
37
  def unmention!(mentioner, mentionable)
36
38
  if mentions?(mentioner, mentionable)
37
39
  mention_for(mentioner, mentionable).destroy_all
40
+ update_counter(mentioner, mentionees_count: -1)
41
+ update_counter(mentionable, mentioners_count: -1)
38
42
  call_after_destroy_hooks(mentioner, mentionable)
39
43
  true
40
44
  else
@@ -2,6 +2,10 @@ module Socialization
2
2
  module ActiveRecordStores
3
3
  module Mixins
4
4
  module Base
5
+ def update_counter(model, counter)
6
+ column_name, _ = counter.first
7
+ model.class.update_counters model.id, counter if model.respond_to?(column_name)
8
+ end
5
9
  end
6
10
  end
7
11
  end
@@ -36,4 +36,4 @@ module Socialization
36
36
  end
37
37
  end
38
38
  end
39
- end
39
+ end
@@ -1,3 +1,3 @@
1
1
  module Socialization
2
- VERSION = "1.1.0"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -22,9 +22,11 @@ Gem::Specification.new do |s|
22
22
 
23
23
  s.add_development_dependency "appraisal"
24
24
  s.add_development_dependency "logger"
25
- s.add_development_dependency "mocha"
26
- s.add_development_dependency "shoulda"
25
+ s.add_development_dependency "rspec", "~> 3.3.0"
26
+ s.add_development_dependency "rspec-mocks"
27
27
  s.add_development_dependency "sqlite3"
28
28
  s.add_development_dependency "yard"
29
29
  s.add_development_dependency "mock_redis"
30
- end
30
+ s.add_development_dependency "guard"
31
+ s.add_development_dependency "guard-rspec"
32
+ end
@@ -0,0 +1,113 @@
1
+ require 'spec_helper'
2
+
3
+ describe Socialization::Follower do
4
+ before(:all) do
5
+ use_ar_store
6
+ @follower = ImAFollower.new
7
+ @followable = ImAFollowable.create
8
+ end
9
+
10
+ describe "#is_follower?" do
11
+ it "returns true" do
12
+ expect(@follower.is_follower?).to be true
13
+ end
14
+ end
15
+
16
+ describe "#follower?" do
17
+ it "returns true" do
18
+ expect(@follower.follower?).to be true
19
+ end
20
+ end
21
+
22
+ describe "#follow!" do
23
+ it "does not accept non-followables" do
24
+ expect { @follower.follow!(:foo) }.to raise_error(Socialization::ArgumentError)
25
+ end
26
+
27
+ it "calls $Follow.follow!" do
28
+ expect($Follow).to receive(:follow!).with(@follower, @followable).once
29
+ @follower.follow!(@followable)
30
+ end
31
+ end
32
+
33
+ describe "#unfollow!" do
34
+ it "does not accept non-followables" do
35
+ expect { @follower.unfollow!(:foo) }.to raise_error(Socialization::ArgumentError)
36
+ end
37
+
38
+ it "calls $Follow.follow!" do
39
+ expect($Follow).to receive(:unfollow!).with(@follower, @followable).once
40
+ @follower.unfollow!(@followable)
41
+ end
42
+ end
43
+
44
+ describe "#toggle_follow!" do
45
+ it "does not accept non-followables" do
46
+ expect { @follower.unfollow!(:foo) }.to raise_error(Socialization::ArgumentError)
47
+ end
48
+
49
+ it "unfollows when following" do
50
+ expect(@follower).to receive(:follows?).with(@followable).once.and_return(true)
51
+ expect(@follower).to receive(:unfollow!).with(@followable).once
52
+ @follower.toggle_follow!(@followable)
53
+ end
54
+
55
+ it "follows when not following" do
56
+ expect(@follower).to receive(:follows?).with(@followable).once.and_return(false)
57
+ expect(@follower).to receive(:follow!).with(@followable).once
58
+ @follower.toggle_follow!(@followable)
59
+ end
60
+ end
61
+
62
+ describe "#follows?" do
63
+ it "does not accept non-followables" do
64
+ expect { @follower.unfollow!(:foo) }.to raise_error(Socialization::ArgumentError)
65
+ end
66
+
67
+ it "calls $Follow.follows?" do
68
+ expect($Follow).to receive(:follows?).with(@follower, @followable).once
69
+ @follower.follows?(@followable)
70
+ end
71
+ end
72
+
73
+ describe "#followables" do
74
+ it "calls $Follow.followables" do
75
+ expect($Follow).to receive(:followables).with(@follower, @followable.class, { :foo => :bar })
76
+ @follower.followables(@followable.class, { :foo => :bar })
77
+ end
78
+ end
79
+
80
+ describe "#followees" do
81
+ it "calls $Follow.followables" do
82
+ expect($Follow).to receive(:followables).with(@follower, @followable.class, { :foo => :bar })
83
+ @follower.followees(@followable.class, { :foo => :bar })
84
+ end
85
+ end
86
+
87
+ describe "#followables_relation" do
88
+ it "calls $Follow.followables_relation" do
89
+ expect($Follow).to receive(:followables_relation).with(@follower, @followable.class, { :foo => :bar })
90
+ @follower.followables_relation(@followable.class, { :foo => :bar })
91
+ end
92
+ end
93
+
94
+ describe "#followees_relation" do
95
+ it "calls $Follow.followables_relation" do
96
+ expect($Follow).to receive(:followables_relation).with(@follower, @followable.class, { :foo => :bar })
97
+ @follower.followees_relation(@followable.class, { :foo => :bar })
98
+ end
99
+ end
100
+
101
+ describe "deleting a follower" do
102
+ before(:all) do
103
+ @follower = ImAFollower.create
104
+ @follower.follow!(@followable)
105
+ end
106
+
107
+ it "removes follow relationships" do
108
+ expect(Socialization.follow_model).to receive(:remove_followables).with(@follower)
109
+ @follower.destroy
110
+ end
111
+ end
112
+
113
+ end
@@ -0,0 +1,105 @@
1
+ require 'spec_helper'
2
+
3
+ describe Socialization::Liker do
4
+ before(:all) do
5
+ use_ar_store
6
+ @liker = ImALiker.new
7
+ @likeable = ImALikeable.create
8
+ end
9
+
10
+ describe "#is_liker?" do
11
+ it "returns true" do
12
+ expect(@liker.is_liker?).to be true
13
+ end
14
+ end
15
+
16
+ describe "#liker?" do
17
+ it "returns true" do
18
+ expect(@liker.liker?).to be true
19
+ end
20
+ end
21
+
22
+ describe "#like!" do
23
+ it "does not accept non-likeables" do
24
+ expect { @liker.like!(:foo) }.to raise_error(Socialization::ArgumentError)
25
+ end
26
+
27
+ it "calls $Like.like!" do
28
+ expect($Like).to receive(:like!).with(@liker, @likeable).once
29
+ @liker.like!(@likeable)
30
+ end
31
+ end
32
+
33
+ describe "#unlike!" do
34
+ it "does not accept non-likeables" do
35
+ expect { @liker.unlike!(:foo) }.to raise_error(Socialization::ArgumentError)
36
+ end
37
+
38
+ it "calls $Like.like!" do
39
+ expect($Like).to receive(:unlike!).with(@liker, @likeable).once
40
+ @liker.unlike!(@likeable)
41
+ end
42
+ end
43
+
44
+ describe "#toggle_like!" do
45
+ it "does not accept non-likeables" do
46
+ expect { @liker.unlike!(:foo) }.to raise_error(Socialization::ArgumentError)
47
+ end
48
+
49
+ it "unlikes when likeing" do
50
+ expect(@liker).to receive(:likes?).with(@likeable).once.and_return(true)
51
+ expect(@liker).to receive(:unlike!).with(@likeable).once
52
+ @liker.toggle_like!(@likeable)
53
+ end
54
+
55
+ it "likes when not likeing" do
56
+ expect(@liker).to receive(:likes?).with(@likeable).once.and_return(false)
57
+ expect(@liker).to receive(:like!).with(@likeable).once
58
+ @liker.toggle_like!(@likeable)
59
+ end
60
+ end
61
+
62
+ describe "#likes?" do
63
+ it "does not accept non-likeables" do
64
+ expect { @liker.unlike!(:foo) }.to raise_error(Socialization::ArgumentError)
65
+ end
66
+
67
+ it "calls $Like.likes?" do
68
+ expect($Like).to receive(:likes?).with(@liker, @likeable).once
69
+ @liker.likes?(@likeable)
70
+ end
71
+ end
72
+
73
+ describe "#likeables" do
74
+ it "calls $Like.likeables" do
75
+ expect($Like).to receive(:likeables).with(@liker, @likeable.class, { :foo => :bar })
76
+ @liker.likeables(@likeable.class, { :foo => :bar })
77
+ end
78
+ end
79
+
80
+ describe "#likees" do
81
+ it "calls $Like.likeables" do
82
+ expect($Like).to receive(:likeables).with(@liker, @likeable.class, { :foo => :bar })
83
+ @liker.likees(@likeable.class, { :foo => :bar })
84
+ end
85
+ end
86
+
87
+ describe "#likeables_relation" do
88
+ it "calls $Follow.likeables_relation" do
89
+ expect($Like).to receive(:likeables_relation).with(@liker, @likeable.class, { :foo => :bar })
90
+ @liker.likeables_relation(@likeable.class, { :foo => :bar })
91
+ end
92
+ end
93
+
94
+ describe "#likees_relation" do
95
+ it "calls $Follow.likeables_relation" do
96
+ expect($Like).to receive(:likeables_relation).with(@liker, @likeable.class, { :foo => :bar })
97
+ @liker.likees_relation(@likeable.class, { :foo => :bar })
98
+ end
99
+ end
100
+
101
+ it "removes like relationships" do
102
+ expect(Socialization.like_model).to receive(:remove_likeables).with(@liker)
103
+ @liker.destroy
104
+ end
105
+ end