ohm-contrib 0.1.2 → 1.0.rc0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/lib/ohm/{contrib/callbacks.rb → callbacks.rb} +20 -65
  2. data/lib/ohm/contrib.rb +9 -17
  3. data/lib/ohm/datatypes.rb +38 -0
  4. data/lib/ohm/{contrib/locking.rb → locking.rb} +1 -2
  5. data/lib/ohm/plugin.rb +54 -0
  6. data/lib/ohm/{contrib/scope.rb → scope.rb} +7 -6
  7. data/lib/ohm/{contrib/slug.rb → slug.rb} +11 -6
  8. data/lib/ohm/{contrib/soft_delete.rb → softdelete.rb} +23 -19
  9. data/lib/ohm/timestamping.rb +41 -0
  10. data/ohm-contrib.gemspec +30 -0
  11. data/rakefile +6 -0
  12. data/test/{instance_callbacks_test.rb → instance_callbacks.rb} +2 -6
  13. data/test/{macro_callbacks_test.rb → macro_callbacks.rb} +1 -9
  14. data/test/plugin.rb +212 -0
  15. data/test/{scope_test.rb → scope.rb} +2 -3
  16. data/test/slug.rb +24 -0
  17. data/test/{soft_delete_test.rb → soft_delete.rb} +13 -3
  18. data/test/{timestamping_test.rb → timestamping.rb} +5 -6
  19. metadata +33 -70
  20. data/Rakefile +0 -8
  21. data/lib/ohm/contrib/active_model_extension.rb +0 -87
  22. data/lib/ohm/contrib/boundaries.rb +0 -41
  23. data/lib/ohm/contrib/date_validations.rb +0 -23
  24. data/lib/ohm/contrib/extra_validations.rb +0 -48
  25. data/lib/ohm/contrib/fulltext_searching.rb +0 -80
  26. data/lib/ohm/contrib/length_validations.rb +0 -32
  27. data/lib/ohm/contrib/number_validations.rb +0 -14
  28. data/lib/ohm/contrib/timestamping.rb +0 -38
  29. data/lib/ohm/contrib/typecast.rb +0 -350
  30. data/lib/ohm/contrib/web_validations.rb +0 -52
  31. data/test/activemodel_test.rb +0 -27
  32. data/test/boundaries_test.rb +0 -45
  33. data/test/callbacks_lint.rb +0 -141
  34. data/test/date_validations_test.rb +0 -29
  35. data/test/fixtures/ascii8bit.txt +0 -1
  36. data/test/fulltext_searching_test.rb +0 -63
  37. data/test/length_validations_test.rb +0 -31
  38. data/test/membership_validation_test.rb +0 -31
  39. data/test/number_validations_test.rb +0 -37
  40. data/test/slug_test.rb +0 -30
  41. data/test/typecast_array_test.rb +0 -146
  42. data/test/typecast_boolean_test.rb +0 -43
  43. data/test/typecast_date_test.rb +0 -82
  44. data/test/typecast_decimal_test.rb +0 -82
  45. data/test/typecast_existing_attribute_test.rb +0 -22
  46. data/test/typecast_float_test.rb +0 -57
  47. data/test/typecast_hash_test.rb +0 -120
  48. data/test/typecast_integer_test.rb +0 -71
  49. data/test/typecast_string_test.rb +0 -43
  50. data/test/typecast_time_test.rb +0 -72
  51. data/test/typecast_timezone_test.rb +0 -37
  52. data/test/web_validations_test.rb +0 -79
data/test/plugin.rb ADDED
@@ -0,0 +1,212 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative "helper"
4
+
5
+ class Comment < Ohm::Model
6
+ include Ohm::Timestamping
7
+ end
8
+
9
+ test "timestamps are added during creation" do
10
+ e = Comment.new
11
+ e.save
12
+
13
+ assert e.created_at
14
+ assert e.updated_at
15
+ end
16
+
17
+ class Server < Ohm::Model
18
+ include Ohm::Locking
19
+ end
20
+
21
+ test "mutex method is added at instance and class level" do
22
+ assert Server.new.respond_to?(:mutex)
23
+ end
24
+
25
+ class Article < Ohm::Model
26
+ include Ohm::Callbacks
27
+
28
+ attribute :title
29
+
30
+ list :comments, Comment
31
+ after :save, :append_comment
32
+
33
+ private
34
+ def before_save
35
+ self.title = title.gsub("<br>", " ")
36
+ end
37
+
38
+ def append_comment
39
+ comments.add(Comment.create)
40
+ end
41
+ end
42
+
43
+ test "class-level, instance level callbacks" do
44
+ a = Article.create(title: "Foo<br>Bar")
45
+
46
+ assert_equal "Foo Bar", a.title
47
+ assert_equal 1, a.comments.size
48
+ end
49
+
50
+ class Post < Ohm::Model
51
+ attribute :title
52
+
53
+ include Ohm::Slug
54
+
55
+ def to_s
56
+ title
57
+ end
58
+ end
59
+
60
+ test "slugging" do
61
+ post = Post.create(title: "Foo Bar Baz")
62
+ assert_equal "1-foo-bar-baz", post.to_param
63
+
64
+ post = Post.create(title: "Décor")
65
+ assert_equal "2-decor", post.to_param
66
+ end
67
+
68
+ class Order < Ohm::Model
69
+ include Ohm::Scope
70
+
71
+ attribute :state
72
+ index :state
73
+
74
+ attribute :deleted
75
+ index :deleted
76
+
77
+ scope do
78
+ def paid
79
+ find(state: "paid")
80
+ end
81
+
82
+ def deleted
83
+ find(deleted: 1)
84
+ end
85
+ end
86
+ end
87
+
88
+ test "scope" do
89
+ paid = Order.create(state: "paid", deleted: nil)
90
+
91
+ assert Order.all.paid.include?(paid)
92
+ assert_equal 0, Order.all.paid.deleted.size
93
+
94
+ paid.update(deleted: 1)
95
+ assert Order.all.paid.deleted.include?(paid)
96
+ end
97
+
98
+ class User < Ohm::Model
99
+ include Ohm::SoftDelete
100
+
101
+ attribute :email
102
+ index :email
103
+ end
104
+
105
+ test "soft delete" do
106
+ user = User.create(email: "a@a.com")
107
+ user.delete
108
+
109
+ assert User.all.empty?
110
+ assert User.deleted.include?(user)
111
+ assert User.find(email: "a@a.com").include?(user)
112
+
113
+ assert user.deleted?
114
+ assert User[user.id] == user
115
+
116
+ user.restore
117
+
118
+ assert User.all.include?(user)
119
+ assert User.deleted.empty?
120
+
121
+ assert ! user.deleted?
122
+ end
123
+
124
+ class Product < Ohm::Model
125
+ include Ohm::DataTypes
126
+
127
+ attribute :name
128
+ attribute :stock, Type::Integer
129
+ attribute :price, Type::Decimal
130
+ attribute :rating, Type::Float
131
+ attribute :bought_at, Type::Time
132
+ attribute :date_released, Type::Date
133
+ attribute :sizes, Type::Hash
134
+ attribute :stores, Type::Array
135
+ attribute :published, Type::Boolean
136
+ end
137
+
138
+ test "typecast" do
139
+ p = Product.new(stock: "1")
140
+
141
+ assert_equal 1, p.stock
142
+ p.save
143
+
144
+ p = Product[p.id]
145
+ assert_equal 1, p.stock
146
+
147
+ time = Time.now.utc
148
+ p = Product.new(bought_at: time)
149
+ assert p.bought_at.kind_of?(Time)
150
+
151
+ p.save
152
+
153
+ p = Product[p.id]
154
+ assert p.bought_at.kind_of?(Time)
155
+ assert_equal time, p.bought_at
156
+
157
+ p = Product.new(date_released: Date.today)
158
+ assert p.date_released.kind_of?(Date)
159
+
160
+ p = Product.new(date_released: "2011-11-22")
161
+ assert p.date_released.kind_of?(Date)
162
+
163
+ p.save
164
+
165
+ p = Product[p.id]
166
+ assert_equal Date.today, p.date_released
167
+
168
+ sizes = { "XS" => 1, "S" => 2, "L" => 3 }
169
+
170
+ p = Product.new(sizes: sizes)
171
+ assert p.sizes.kind_of?(Hash)
172
+
173
+ p.save
174
+
175
+ p = Product[p.id]
176
+ assert_equal sizes, p.sizes
177
+
178
+ stores = ["walmart", "marshalls", "jcpenny"]
179
+ p = Product.new(stores: stores)
180
+ assert p.stores.kind_of?(Array)
181
+
182
+ p.save
183
+
184
+ p = Product[p.id]
185
+ assert_equal stores, p.stores
186
+
187
+ p = Product.new(price: 0.001)
188
+
189
+ x = 0
190
+ 1000.times { x += p.price }
191
+ assert_equal 1, x
192
+
193
+ p.save
194
+
195
+ p = Product[p.id]
196
+ assert_equal 0.001, p.price
197
+
198
+ p = Product.new(rating: 4.5)
199
+ assert p.rating.kind_of?(Float)
200
+
201
+ p.save
202
+ p = Product[p.id]
203
+ assert_equal 4.5, p.rating
204
+
205
+ p = Product.new(published: 1)
206
+ assert_equal true, p.published
207
+
208
+ p.save
209
+
210
+ p = Product[p.id]
211
+ assert_equal true, p.published
212
+ end
@@ -1,6 +1,6 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require File.expand_path("./helper", File.dirname(__FILE__))
3
+ require_relative "helper"
4
4
 
5
5
  class Post < Ohm::Model
6
6
  set :comments, Comment
@@ -72,5 +72,4 @@ test "isolated from List" do
72
72
  assert_raise NoMethodError do
73
73
  video.comments.rejected
74
74
  end
75
- end
76
-
75
+ end
data/test/slug.rb ADDED
@@ -0,0 +1,24 @@
1
+ # encoding: UTF-8
2
+
3
+ require_relative "helper"
4
+
5
+ class Article < Ohm::Model
6
+ include Ohm::Slug
7
+ attribute :title
8
+
9
+ def to_s
10
+ title
11
+ end
12
+ end
13
+
14
+ test "to_param" do
15
+ article = Article.create(title: "A very Unique and Interesting String?'")
16
+
17
+ assert '1-a-very-unique-and-interesting-string' == article.to_param
18
+ end
19
+
20
+ test "finding" do
21
+ article = Article.create(title: "A very Unique and Interesting String?'")
22
+
23
+ assert 1 == Article["1-a-very-unique-and-interesting-string"].id
24
+ end
@@ -32,15 +32,15 @@ test "all excludes deleted records" do
32
32
  assert Person.all.empty?
33
33
  end
34
34
 
35
- test "find excludes deleted records" do
35
+ test "find does not exclude deleted records" do
36
36
  person = Person.create(:name => "matz")
37
37
 
38
38
  assert Person.find(:name => "matz").first == person
39
39
 
40
40
  person.delete
41
41
 
42
- assert Person.find(:name => "matz").empty?
43
- assert Person.all.find(:name => "matz").empty?
42
+ assert Person.find(:name => "matz").include?(person)
43
+ assert Person.all.find(:name => "matz").include?(person)
44
44
  end
45
45
 
46
46
  test "find with many criteria excludes deleted records" do
@@ -69,4 +69,14 @@ test "Model[n] can be used to retrieve deleted records" do
69
69
 
70
70
  assert Person[person.id].deleted?
71
71
  assert Person[person.id].name == "matz"
72
+ end
73
+
74
+ test "restoring" do
75
+ person = Person.create(:name => "matz")
76
+ person.delete
77
+
78
+ assert Person.all.empty?
79
+
80
+ person.restore
81
+ assert Person.all.include?(person)
72
82
  end
@@ -15,8 +15,8 @@ test "on create" do
15
15
  person = Person.create
16
16
  person = Person[person.id]
17
17
 
18
- assert NOW.to_s == person.created_at
19
- assert NOW.to_s == person.updated_at
18
+ assert NOW == person.created_at
19
+ assert NOW == person.updated_at
20
20
  end
21
21
 
22
22
  test "on update" do
@@ -26,7 +26,6 @@ test "on update" do
26
26
  person.save
27
27
  person = Person[person.id]
28
28
 
29
- assert NOW.to_s == person.created_at
30
- assert Time.utc(2010, 5, 13).to_s == person.updated_at
31
- end
32
-
29
+ assert NOW == person.created_at
30
+ assert Time.utc(2010, 5, 13) == person.updated_at
31
+ end
metadata CHANGED
@@ -1,19 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ohm-contrib
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
5
- prerelease:
4
+ version: 1.0.rc0
5
+ prerelease: 4
6
6
  platform: ruby
7
7
  authors:
8
8
  - Cyril David
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-11-21 00:00:00.000000000Z
12
+ date: 2011-11-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ohm
16
- requirement: &2164476460 !ruby/object:Gem::Requirement
16
+ requirement: &2164457940 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2164476460
24
+ version_requirements: *2164457940
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: cutest
27
- requirement: &2164475860 !ruby/object:Gem::Requirement
27
+ requirement: &2164457380 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *2164475860
35
+ version_requirements: *2164457380
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: redis
38
- requirement: &2164474640 !ruby/object:Gem::Requirement
38
+ requirement: &2164472240 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,21 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *2164474640
47
- - !ruby/object:Gem::Dependency
48
- name: lunar
49
- requirement: &2164474060 !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: *2164474060
46
+ version_requirements: *2164472240
58
47
  - !ruby/object:Gem::Dependency
59
48
  name: override
60
- requirement: &2164473440 !ruby/object:Gem::Requirement
49
+ requirement: &2164471540 !ruby/object:Gem::Requirement
61
50
  none: false
62
51
  requirements:
63
52
  - - ! '>='
@@ -65,7 +54,7 @@ dependencies:
65
54
  version: '0'
66
55
  type: :development
67
56
  prerelease: false
68
- version_requirements: *2164473440
57
+ version_requirements: *2164471540
69
58
  description: Includes a couple of core functions such as callbacks, timestamping,
70
59
  typecasting and lots of generic validation routines.
71
60
  email: cyx.ucron@gmail.com
@@ -73,53 +62,27 @@ executables: []
73
62
  extensions: []
74
63
  extra_rdoc_files: []
75
64
  files:
76
- - lib/ohm/contrib/active_model_extension.rb
77
- - lib/ohm/contrib/boundaries.rb
78
- - lib/ohm/contrib/callbacks.rb
79
- - lib/ohm/contrib/date_validations.rb
80
- - lib/ohm/contrib/extra_validations.rb
81
- - lib/ohm/contrib/fulltext_searching.rb
82
- - lib/ohm/contrib/length_validations.rb
83
- - lib/ohm/contrib/locking.rb
84
- - lib/ohm/contrib/number_validations.rb
85
- - lib/ohm/contrib/scope.rb
86
- - lib/ohm/contrib/slug.rb
87
- - lib/ohm/contrib/soft_delete.rb
88
- - lib/ohm/contrib/timestamping.rb
89
- - lib/ohm/contrib/typecast.rb
90
- - lib/ohm/contrib/web_validations.rb
91
- - lib/ohm/contrib.rb
92
- - README.markdown
93
65
  - LICENSE
94
- - Rakefile
95
- - test/activemodel_test.rb
96
- - test/boundaries_test.rb
97
- - test/callbacks_lint.rb
98
- - test/date_validations_test.rb
99
- - test/fixtures/ascii8bit.txt
100
- - test/fulltext_searching_test.rb
66
+ - README.markdown
67
+ - rakefile
68
+ - lib/ohm/callbacks.rb
69
+ - lib/ohm/contrib.rb
70
+ - lib/ohm/datatypes.rb
71
+ - lib/ohm/locking.rb
72
+ - lib/ohm/plugin.rb
73
+ - lib/ohm/scope.rb
74
+ - lib/ohm/slug.rb
75
+ - lib/ohm/softdelete.rb
76
+ - lib/ohm/timestamping.rb
77
+ - ohm-contrib.gemspec
101
78
  - test/helper.rb
102
- - test/instance_callbacks_test.rb
103
- - test/length_validations_test.rb
104
- - test/macro_callbacks_test.rb
105
- - test/membership_validation_test.rb
106
- - test/number_validations_test.rb
107
- - test/scope_test.rb
108
- - test/slug_test.rb
109
- - test/soft_delete_test.rb
110
- - test/timestamping_test.rb
111
- - test/typecast_array_test.rb
112
- - test/typecast_boolean_test.rb
113
- - test/typecast_date_test.rb
114
- - test/typecast_decimal_test.rb
115
- - test/typecast_existing_attribute_test.rb
116
- - test/typecast_float_test.rb
117
- - test/typecast_hash_test.rb
118
- - test/typecast_integer_test.rb
119
- - test/typecast_string_test.rb
120
- - test/typecast_time_test.rb
121
- - test/typecast_timezone_test.rb
122
- - test/web_validations_test.rb
79
+ - test/instance_callbacks.rb
80
+ - test/macro_callbacks.rb
81
+ - test/plugin.rb
82
+ - test/scope.rb
83
+ - test/slug.rb
84
+ - test/soft_delete.rb
85
+ - test/timestamping.rb
123
86
  homepage: http://github.com/cyx/ohm-contrib
124
87
  licenses: []
125
88
  post_install_message:
@@ -135,12 +98,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
135
98
  required_rubygems_version: !ruby/object:Gem::Requirement
136
99
  none: false
137
100
  requirements:
138
- - - ! '>='
101
+ - - ! '>'
139
102
  - !ruby/object:Gem::Version
140
- version: '0'
103
+ version: 1.3.1
141
104
  requirements: []
142
105
  rubyforge_project: ohm-contrib
143
- rubygems_version: 1.8.11
106
+ rubygems_version: 1.8.10
144
107
  signing_key:
145
108
  specification_version: 2
146
109
  summary: A collection of decoupled drop-in modules for Ohm.
data/Rakefile DELETED
@@ -1,8 +0,0 @@
1
- require 'cutest'
2
-
3
- task :test do
4
- $:.unshift('./lib', './test')
5
-
6
- Cutest.run(Dir['./test/*_test.rb'])
7
- end
8
-
@@ -1,87 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require "active_model"
4
-
5
- # Extension for ActiveModel compatibility
6
- #
7
- # @example
8
- #
9
- # class Post < Ohm::Model
10
- # include Ohm::ActiveModelExtension
11
- # end
12
- module Ohm
13
- module ActiveModelExtension
14
- def self.included(model)
15
- model.extend ActiveModel::Naming
16
- end
17
-
18
- def to_model
19
- ActiveModelInterface.new(self)
20
- end
21
- end
22
-
23
- class ActiveModelInterface
24
- extend ActiveModel::Naming
25
-
26
- def initialize(model)
27
- @model = model
28
- end
29
-
30
- def to_model
31
- self
32
- end
33
-
34
- def valid?
35
- @model.valid?
36
- end
37
-
38
- def new_record?
39
- @model.new?
40
- end
41
-
42
- def destroyed?
43
- false
44
- end
45
-
46
- def to_key
47
- [@model.id] if persisted?
48
- end
49
-
50
- def persisted?
51
- ! new_record?
52
- end
53
-
54
- def to_param
55
- if persisted?
56
- @model.respond_to?(:to_param) ?
57
- @model.to_param :
58
- @model.id
59
- end
60
- end
61
-
62
- def errors
63
- Errors.new(@model.class.to_reference, @model.errors)
64
- end
65
-
66
- class Errors
67
- def initialize(scope, errors)
68
- @scope = scope
69
- @errors = Hash.new { |hash, key| hash[key] = [] }
70
-
71
- errors.each do |key, value|
72
- @errors[key] << value
73
- end
74
- end
75
-
76
- def [](key)
77
- @errors[key]
78
- end
79
-
80
- def full_messages
81
- @errors.map do |key, value|
82
- I18n::t("ohm.%s.%s.%s" % [@scope, key, value])
83
- end
84
- end
85
- end
86
- end
87
- end
@@ -1,41 +0,0 @@
1
- module Ohm
2
- # Dirt cheap ::first and ::last support.
3
- #
4
- # @example
5
- #
6
- # class Post < Ohm::Model
7
- # include Ohm::Boundaries
8
- # end
9
- #
10
- # post1 = Post.create
11
- # post2 = Post.create
12
- # post1 == Post.first
13
- # # => true
14
- #
15
- # post2 == Post.last
16
- # # => true
17
- module Boundaries
18
- def self.included(base)
19
- base.extend ClassMethods
20
- end
21
-
22
- module ClassMethods
23
- def first(opts = {})
24
- if opts.any?
25
- find(opts).first
26
- else
27
- all.first(opts)
28
- end
29
- end
30
-
31
- def last(opts = {})
32
- if opts.any?
33
- find(opts).first(:order => "DESC")
34
- else
35
- all.first(:order => "DESC")
36
- end
37
- end
38
- end
39
- end
40
- end
41
-
@@ -1,23 +0,0 @@
1
- require 'date'
2
-
3
- module Ohm
4
- module DateValidations
5
- DATE_REGEX = /\A([0-9]{4})-([01]?[0-9])-([0123]?[0-9])\z/
6
-
7
- def assert_date(att, error = [att, :not_date])
8
- if assert_format att, DATE_REGEX, error
9
- m = send(att).to_s.match(DATE_REGEX)
10
- assert is_date_parseable?(m[1], m[2], m[3]), error
11
- end
12
- end
13
-
14
- private
15
- def is_date_parseable?(year, month, day)
16
- Date.new(year.to_i, month.to_i, day.to_i)
17
- rescue ArgumentError
18
- return false
19
- else
20
- return true
21
- end
22
- end
23
- end
@@ -1,48 +0,0 @@
1
- module Ohm
2
- # Includes Ohm::NumberValidations and Ohm::WebValidations.
3
- #
4
- # @example
5
- #
6
- # class Post < Ohm::Model
7
- # include Ohm::ExtraValidations
8
- #
9
- # attribute :price
10
- # attribute :state
11
- # attribute :slug
12
- # attribute :author_email
13
- # attribute :url
14
- # attribute :ipaddr
15
- # attribute :birthday
16
- #
17
- # def validate
18
- # super
19
- #
20
- # assert_decimal :price
21
- # assert_member :state, ['published', 'unpublished']
22
- # assert_ipaddr :ipaddr
23
- # assert_url :url
24
- # assert_email :author_email
25
- # assert_date :birthday
26
- # assert_min_length :slug, 10
27
- # assert_max_length :slug, 25
28
- # end
29
- # end
30
- #
31
- # post = Post.new
32
- # post.valid?
33
- # post.errors
34
- # # [[:price, :not_decimal], [:state, :not_member], [:ipaddr, :not_ipaddr],
35
- # # [:url, :not_url], [:author_email, :not_email],[:slug, :too_short]]
36
- module ExtraValidations
37
- include NumberValidations
38
- include WebValidations
39
- include DateValidations
40
- include LengthValidations
41
-
42
- protected
43
- def assert_member(att, set, error = [att, :not_member])
44
- assert set.include?(send(att)), error
45
- end
46
- end
47
- end
48
-