composite_primary_keys 7.0.16 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/History.rdoc +600 -623
  3. data/lib/composite_primary_keys.rb +113 -115
  4. data/lib/composite_primary_keys/associations/association.rb +23 -23
  5. data/lib/composite_primary_keys/associations/association_scope.rb +73 -77
  6. data/lib/composite_primary_keys/associations/collection_association.rb +15 -0
  7. data/lib/composite_primary_keys/associations/has_many_association.rb +69 -56
  8. data/lib/composite_primary_keys/associations/has_many_through_association.rb +30 -28
  9. data/lib/composite_primary_keys/associations/join_dependency.rb +87 -89
  10. data/lib/composite_primary_keys/associations/join_dependency/join_association.rb +22 -22
  11. data/lib/composite_primary_keys/associations/preloader/association.rb +90 -78
  12. data/lib/composite_primary_keys/associations/preloader/belongs_to.rb +19 -19
  13. data/lib/composite_primary_keys/associations/singular_association.rb +15 -0
  14. data/lib/composite_primary_keys/attribute_methods.rb +9 -0
  15. data/lib/composite_primary_keys/attribute_methods/dirty.rb +29 -26
  16. data/lib/composite_primary_keys/attribute_methods/read.rb +19 -34
  17. data/lib/composite_primary_keys/attribute_methods/write.rb +30 -36
  18. data/lib/composite_primary_keys/attribute_set/builder.rb +20 -0
  19. data/lib/composite_primary_keys/base.rb +135 -129
  20. data/lib/composite_primary_keys/composite_arrays.rb +30 -30
  21. data/lib/composite_primary_keys/composite_predicates.rb +50 -50
  22. data/lib/composite_primary_keys/composite_relation.rb +48 -48
  23. data/lib/composite_primary_keys/connection_adapters/abstract/connection_specification_changes.rb +2 -4
  24. data/lib/composite_primary_keys/connection_adapters/postgresql_adapter.rb +46 -60
  25. data/lib/composite_primary_keys/core.rb +69 -47
  26. data/lib/composite_primary_keys/fixtures.rb +22 -22
  27. data/lib/composite_primary_keys/persistence.rb +56 -60
  28. data/lib/composite_primary_keys/relation.rb +68 -56
  29. data/lib/composite_primary_keys/relation/calculations.rb +79 -75
  30. data/lib/composite_primary_keys/relation/finder_methods.rb +175 -196
  31. data/lib/composite_primary_keys/relation/query_methods.rb +40 -40
  32. data/lib/composite_primary_keys/sanitization.rb +52 -52
  33. data/lib/composite_primary_keys/validations/uniqueness.rb +36 -37
  34. data/lib/composite_primary_keys/version.rb +8 -8
  35. data/tasks/databases/oracle.rake +25 -25
  36. data/tasks/databases/sqlserver.rake +27 -40
  37. data/test/abstract_unit.rb +113 -113
  38. data/test/connections/databases.ci.yml +15 -15
  39. data/test/connections/databases.example.yml +18 -18
  40. data/test/connections/native_oracle/connection.rb +11 -11
  41. data/test/connections/native_oracle_enhanced/connection.rb +16 -16
  42. data/test/connections/native_sqlserver/connection.rb +11 -14
  43. data/test/fixtures/comment.rb +7 -7
  44. data/test/fixtures/db_definitions/db2-create-tables.sql +125 -126
  45. data/test/fixtures/db_definitions/db2-drop-tables.sql +18 -18
  46. data/test/fixtures/db_definitions/mysql.sql +207 -208
  47. data/test/fixtures/db_definitions/oracle.drop.sql +45 -45
  48. data/test/fixtures/db_definitions/oracle.sql +222 -223
  49. data/test/fixtures/db_definitions/postgresql.sql +209 -210
  50. data/test/fixtures/db_definitions/sqlite.sql +196 -197
  51. data/test/fixtures/db_definitions/sqlserver.drop.sql +91 -94
  52. data/test/fixtures/db_definitions/sqlserver.sql +225 -232
  53. data/test/fixtures/dorm.rb +2 -2
  54. data/test/fixtures/employee.rb +5 -5
  55. data/test/fixtures/membership.rb +6 -6
  56. data/test/fixtures/membership_statuses.yml +16 -16
  57. data/test/fixtures/memberships.yml +10 -10
  58. data/test/fixtures/product_tariffs.yml +14 -14
  59. data/test/fixtures/reference_code.rb +7 -7
  60. data/test/fixtures/restaurants_suburb.rb +2 -2
  61. data/test/fixtures/suburb.rb +5 -5
  62. data/test/fixtures/topic.rb +5 -5
  63. data/test/fixtures/topic_source.rb +6 -6
  64. data/test/fixtures/topic_sources.yml +3 -3
  65. data/test/fixtures/topics.yml +8 -8
  66. data/test/fixtures/users.yml +10 -10
  67. data/test/test_associations.rb +295 -275
  68. data/test/test_attribute_methods.rb +63 -63
  69. data/test/test_attributes.rb +60 -60
  70. data/test/test_calculations.rb +37 -42
  71. data/test/test_callbacks.rb +99 -99
  72. data/test/test_create.rb +112 -112
  73. data/test/test_delete.rb +148 -152
  74. data/test/test_delete_all.rb +28 -26
  75. data/test/test_dumpable.rb +15 -15
  76. data/test/test_enum.rb +21 -20
  77. data/test/test_equal.rb +26 -26
  78. data/test/test_find.rb +118 -118
  79. data/test/test_habtm.rb +113 -113
  80. data/test/test_nested_attributes.rb +124 -124
  81. data/test/test_polymorphic.rb +26 -26
  82. data/test/test_predicates.rb +40 -40
  83. data/test/test_santiago.rb +23 -23
  84. data/test/test_suite.rb +33 -34
  85. data/test/test_touch.rb +23 -23
  86. data/test/test_tutorial_example.rb +21 -21
  87. data/test/test_update.rb +71 -71
  88. metadata +9 -13
  89. data/lib/composite_primary_keys/arel/visitors/to_sql.rb +0 -20
  90. data/lib/composite_primary_keys/associations/has_and_belongs_to_many_association.rb +0 -59
  91. data/lib/composite_primary_keys/associations/join_dependency/join_part.rb +0 -39
  92. data/lib/composite_primary_keys/associations/preloader/has_and_belongs_to_many.rb +0 -46
  93. data/lib/composite_primary_keys/connection_adapters/sqlserver_adapter.rb +0 -17
  94. data/lib/composite_primary_keys/locking/optimistic.rb +0 -55
  95. data/test/test_optimistic.rb +0 -18
data/test/test_touch.rb CHANGED
@@ -1,23 +1,23 @@
1
- # Test cases devised by Santiago that broke the Composite Primary Keys
2
- # code at one point in time. But no more!!!
3
- require File.expand_path('../abstract_unit', __FILE__)
4
-
5
- class TestTouch < ActiveSupport::TestCase
6
- fixtures :products, :tariffs
7
-
8
- def test_touching_a_record_updates_its_timestamp
9
- tariff = tariffs(:flat)
10
- previous_amount = tariff.amount
11
- previously_updated_at = tariff.updated_at
12
-
13
- tariff.amount = previous_amount + 1
14
- sleep 1.0 # we need to sleep for 1 second because the times updated (on mysql, at least) are only precise to 1 second.
15
- tariff.touch
16
- assert_not_equal previously_updated_at, tariff.updated_at
17
- assert_equal previous_amount + 1, tariff.amount
18
- assert tariff.amount_changed?, 'tarif amount should have changed'
19
- assert tariff.changed?, 'tarif should be marked as changed'
20
- tariff.reload
21
- assert_not_equal previously_updated_at, tariff.updated_at
22
- end
23
- end
1
+ # Test cases devised by Santiago that broke the Composite Primary Keys
2
+ # code at one point in time. But no more!!!
3
+ require File.expand_path('../abstract_unit', __FILE__)
4
+
5
+ class TestTouch < ActiveSupport::TestCase
6
+ fixtures :products, :tariffs
7
+
8
+ def test_touching_a_record_updates_its_timestamp
9
+ tariff = tariffs(:flat)
10
+ previous_amount = tariff.amount
11
+ previously_updated_at = tariff.updated_at
12
+
13
+ tariff.amount = previous_amount + 1
14
+ sleep 1.0 # we need to sleep for 1 second because the times updated (on mysql, at least) are only precise to 1 second.
15
+ tariff.touch
16
+ assert_not_equal previously_updated_at, tariff.updated_at
17
+ assert_equal previous_amount + 1, tariff.amount
18
+ assert tariff.amount_changed?, 'tarif amount should have changed'
19
+ assert tariff.changed?, 'tarif should be marked as changed'
20
+ tariff.reload
21
+ assert_not_equal previously_updated_at, tariff.updated_at
22
+ end
23
+ end
@@ -1,22 +1,22 @@
1
- require File.expand_path('../abstract_unit', __FILE__)
2
-
3
- class TestTutorialExample < ActiveSupport::TestCase
4
- fixtures :users, :groups, :memberships, :membership_statuses
5
-
6
- def test_membership
7
- assert(membership = Membership.find([1, 1]), "Cannot find a membership")
8
- assert(membership.user)
9
- assert(membership.group)
10
- end
11
-
12
- def test_status
13
- assert(membership = Membership.find([1, 1]), "Cannot find a membership")
14
- assert(statuses = membership.statuses, "No has_many association to status")
15
- assert_equal(membership, statuses.first.membership)
16
- end
17
-
18
- def test_count
19
- assert(membership = Membership.find([1, 1]), "Cannot find a membership")
20
- assert_equal(1, membership.statuses.count)
21
- end
1
+ require File.expand_path('../abstract_unit', __FILE__)
2
+
3
+ class TestTutorialExample < ActiveSupport::TestCase
4
+ fixtures :users, :groups, :memberships, :membership_statuses
5
+
6
+ def test_membership
7
+ assert(membership = Membership.find([1, 1]), "Cannot find a membership")
8
+ assert(membership.user)
9
+ assert(membership.group)
10
+ end
11
+
12
+ def test_status
13
+ assert(membership = Membership.find([1, 1]), "Cannot find a membership")
14
+ assert(statuses = membership.statuses, "No has_many association to status")
15
+ assert_equal(membership, statuses.first.membership)
16
+ end
17
+
18
+ def test_count
19
+ assert(membership = Membership.find([1, 1]), "Cannot find a membership")
20
+ assert_equal(1, membership.statuses.count)
21
+ end
22
22
  end
data/test/test_update.rb CHANGED
@@ -1,71 +1,71 @@
1
- require File.expand_path('../abstract_unit', __FILE__)
2
-
3
- class TestUpdate < ActiveSupport::TestCase
4
- fixtures :reference_types, :reference_codes
5
-
6
- CLASSES = {
7
- :single => {
8
- :class => ReferenceType,
9
- :primary_keys => :reference_type_id,
10
- :update => { :description => 'RT Desc' },
11
- },
12
- :dual => {
13
- :class => ReferenceCode,
14
- :primary_keys => [:reference_type_id, :reference_code],
15
- :update => { :description => 'RT Desc' },
16
- },
17
- }
18
-
19
- def setup
20
- self.class.classes = CLASSES
21
- end
22
-
23
- def test_setup
24
- testing_with do
25
- assert_not_nil @klass_info[:update]
26
- end
27
- end
28
-
29
- def test_update_attributes
30
- testing_with do
31
- assert(@first.update_attributes(@klass_info[:update]))
32
- assert(@first.reload)
33
- @klass_info[:update].each_pair do |attr_name, new_value|
34
- assert_equal(new_value, @first[attr_name])
35
- end
36
- end
37
- end
38
-
39
- def test_update_primary_key
40
- obj = ReferenceCode.find([1,1])
41
- obj.reference_type_id = 2
42
- obj.reference_code = 3
43
- assert(obj.primary_key_changed?)
44
- assert_equal({"reference_type_id" => 1, "reference_code" => 1}, obj.primary_key_was)
45
- assert_equal({"reference_type_id" => 2, "reference_code" => 3}, obj.ids_hash)
46
- assert(obj.save)
47
- assert(obj.reload)
48
- assert_equal(2, obj.reference_type_id)
49
- assert_equal(3, obj.reference_code)
50
- assert_equal({"reference_type_id" => 2, "reference_code" => 3}, obj.ids_hash)
51
- assert_equal([2, 3], obj.id)
52
- end
53
-
54
- def test_update_attribute
55
- obj = ReferenceType.find(1)
56
- obj[:abbreviation] = 'a'
57
- obj['abbreviation'] = 'b'
58
- assert(obj.save)
59
- assert(obj.reload)
60
- assert_equal('b', obj.abbreviation)
61
- end
62
-
63
- def test_update_all
64
- assert_nothing_raised do
65
- reference_code = ReferenceCode.create
66
- primary_key = reference_code.class.primary_key
67
- ReferenceCode.where(primary_key => reference_code[primary_key]).
68
- update_all(description: 'random value')
69
- end
70
- end
71
- end
1
+ require File.expand_path('../abstract_unit', __FILE__)
2
+
3
+ class TestUpdate < ActiveSupport::TestCase
4
+ fixtures :reference_types, :reference_codes
5
+
6
+ CLASSES = {
7
+ :single => {
8
+ :class => ReferenceType,
9
+ :primary_keys => :reference_type_id,
10
+ :update => { :description => 'RT Desc' },
11
+ },
12
+ :dual => {
13
+ :class => ReferenceCode,
14
+ :primary_keys => [:reference_type_id, :reference_code],
15
+ :update => { :description => 'RT Desc' },
16
+ },
17
+ }
18
+
19
+ def setup
20
+ self.class.classes = CLASSES
21
+ end
22
+
23
+ def test_setup
24
+ testing_with do
25
+ assert_not_nil @klass_info[:update]
26
+ end
27
+ end
28
+
29
+ def test_update_attributes
30
+ testing_with do
31
+ assert(@first.update_attributes(@klass_info[:update]))
32
+ assert(@first.reload)
33
+ @klass_info[:update].each_pair do |attr_name, new_value|
34
+ assert_equal(new_value, @first[attr_name])
35
+ end
36
+ end
37
+ end
38
+
39
+ def test_update_primary_key
40
+ obj = ReferenceCode.find([1,1])
41
+ obj.reference_type_id = 2
42
+ obj.reference_code = 3
43
+ assert(obj.primary_key_changed?)
44
+ assert_equal({"reference_type_id" => 1, "reference_code" => 1}, obj.primary_key_was)
45
+ assert_equal({"reference_type_id" => 2, "reference_code" => 3}, obj.ids_hash)
46
+ assert(obj.save)
47
+ assert(obj.reload)
48
+ assert_equal(2, obj.reference_type_id)
49
+ assert_equal(3, obj.reference_code)
50
+ assert_equal({"reference_type_id" => 2, "reference_code" => 3}, obj.ids_hash)
51
+ assert_equal([2, 3], obj.id)
52
+ end
53
+
54
+ def test_update_attribute
55
+ obj = ReferenceType.find(1)
56
+ obj[:abbreviation] = 'a'
57
+ obj['abbreviation'] = 'b'
58
+ assert(obj.save)
59
+ assert(obj.reload)
60
+ assert_equal('b', obj.abbreviation)
61
+ end
62
+
63
+ def test_update_all
64
+ assert_nothing_raised do
65
+ reference_code = ReferenceCode.create
66
+ primary_key = reference_code.class.primary_key
67
+ ReferenceCode.where(primary_key => reference_code[primary_key]).
68
+ update_all(description: 'random value')
69
+ end
70
+ end
71
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: composite_primary_keys
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.0.16
4
+ version: 8.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charlie Savage
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-13 00:00:00.000000000 Z
11
+ date: 2015-01-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 4.1.7
19
+ version: 4.2.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 4.1.7
26
+ version: 4.2.0
27
27
  description: Composite key support for ActiveRecord
28
28
  email:
29
29
  executables: []
@@ -35,22 +35,22 @@ files:
35
35
  - README_DB2.rdoc
36
36
  - Rakefile
37
37
  - lib/composite_primary_keys.rb
38
- - lib/composite_primary_keys/arel/visitors/to_sql.rb
39
38
  - lib/composite_primary_keys/associations/association.rb
40
39
  - lib/composite_primary_keys/associations/association_scope.rb
41
- - lib/composite_primary_keys/associations/has_and_belongs_to_many_association.rb
40
+ - lib/composite_primary_keys/associations/collection_association.rb
42
41
  - lib/composite_primary_keys/associations/has_many_association.rb
43
42
  - lib/composite_primary_keys/associations/has_many_through_association.rb
44
43
  - lib/composite_primary_keys/associations/join_dependency.rb
45
44
  - lib/composite_primary_keys/associations/join_dependency/join_association.rb
46
- - lib/composite_primary_keys/associations/join_dependency/join_part.rb
47
45
  - lib/composite_primary_keys/associations/preloader/association.rb
48
46
  - lib/composite_primary_keys/associations/preloader/belongs_to.rb
49
- - lib/composite_primary_keys/associations/preloader/has_and_belongs_to_many.rb
47
+ - lib/composite_primary_keys/associations/singular_association.rb
48
+ - lib/composite_primary_keys/attribute_methods.rb
50
49
  - lib/composite_primary_keys/attribute_methods/dirty.rb
51
50
  - lib/composite_primary_keys/attribute_methods/primary_key.rb
52
51
  - lib/composite_primary_keys/attribute_methods/read.rb
53
52
  - lib/composite_primary_keys/attribute_methods/write.rb
53
+ - lib/composite_primary_keys/attribute_set/builder.rb
54
54
  - lib/composite_primary_keys/base.rb
55
55
  - lib/composite_primary_keys/composite_arrays.rb
56
56
  - lib/composite_primary_keys/composite_predicates.rb
@@ -58,11 +58,9 @@ files:
58
58
  - lib/composite_primary_keys/connection_adapters/abstract/connection_specification_changes.rb
59
59
  - lib/composite_primary_keys/connection_adapters/abstract_adapter.rb
60
60
  - lib/composite_primary_keys/connection_adapters/postgresql_adapter.rb
61
- - lib/composite_primary_keys/connection_adapters/sqlserver_adapter.rb
62
61
  - lib/composite_primary_keys/core.rb
63
62
  - lib/composite_primary_keys/dirty.rb
64
63
  - lib/composite_primary_keys/fixtures.rb
65
- - lib/composite_primary_keys/locking/optimistic.rb
66
64
  - lib/composite_primary_keys/model_schema.rb
67
65
  - lib/composite_primary_keys/nested_attributes.rb
68
66
  - lib/composite_primary_keys/persistence.rb
@@ -191,7 +189,6 @@ files:
191
189
  - test/test_ids.rb
192
190
  - test/test_miscellaneous.rb
193
191
  - test/test_nested_attributes.rb
194
- - test/test_optimistic.rb
195
192
  - test/test_pagination.rb
196
193
  - test/test_polymorphic.rb
197
194
  - test/test_predicates.rb
@@ -222,7 +219,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
222
219
  version: '0'
223
220
  requirements: []
224
221
  rubyforge_project:
225
- rubygems_version: 2.4.5.1
222
+ rubygems_version: 2.4.5
226
223
  signing_key:
227
224
  specification_version: 4
228
225
  summary: Composite key support for ActiveRecord
@@ -252,7 +249,6 @@ test_files:
252
249
  - test/test_ids.rb
253
250
  - test/test_miscellaneous.rb
254
251
  - test/test_nested_attributes.rb
255
- - test/test_optimistic.rb
256
252
  - test/test_pagination.rb
257
253
  - test/test_polymorphic.rb
258
254
  - test/test_predicates.rb
@@ -1,20 +0,0 @@
1
- module Arel
2
- module Visitors
3
- class ToSql
4
- def visit_Arel_Nodes_In o, a
5
- if Array === o.right && o.right.empty?
6
- '1=0'
7
- else
8
- a = o.left if Arel::Attributes::Attribute === o.left
9
- # CPK
10
- #"#{visit o.left, a} IN (#{visit o.right, a})"
11
- if o.left.name.is_a?(Array)
12
- "(#{visit o.left, a}) IN (#{visit o.right, a})"
13
- else
14
- "#{visit o.left, a} IN (#{visit o.right, a})"
15
- end
16
- end
17
- end
18
- end
19
- end
20
- end
@@ -1,59 +0,0 @@
1
- module ActiveRecord
2
- module Associations
3
- class HasAndBelongsToManyAssociation
4
- def insert_record(record, validate = true, raise = false)
5
- if record.new_record?
6
- if raise
7
- record.save!(:validate => validate)
8
- else
9
- return unless record.save(:validate => validate)
10
- end
11
- end
12
-
13
- if options[:insert_sql]
14
- owner.class.connection.insert(interpolate(options[:insert_sql], record))
15
- else
16
- # CPK
17
- #stmt = join_table.compile_insert(
18
- # join_table[reflection.foreign_key] => owner.id,
19
- # join_table[reflection.association_foreign_key] => record.id
20
- #)
21
- join_values = Hash.new
22
- Array(reflection.foreign_key).zip(Array(owner.id)) do |name, value|
23
- attribute = join_table[name]
24
- join_values[attribute] = value
25
- end
26
-
27
- Array(reflection.association_foreign_key).zip(Array(record.id)) do |name, value|
28
- attribute = join_table[name]
29
- join_values[attribute] = value
30
- end
31
-
32
- stmt = join_table.compile_insert(join_values)
33
-
34
- owner.class.connection.insert stmt.to_sql
35
- end
36
-
37
- record
38
- end
39
-
40
- def delete_records(records, method)
41
- if sql = options[:delete_sql]
42
- records.each { |record| owner.class.connection.delete(interpolate(sql, record)) }
43
- else
44
- relation = join_table
45
- # CPK
46
- # stmt = relation.where(relation[reflection.foreign_key].eq(owner.id).
47
- # and(relation[reflection.association_foreign_key].in(records.map { |x| x.id }.compact))
48
- #).compile_delete
49
-
50
- predicate1 = cpk_id_predicate(relation, Array(reflection.foreign_key), Array(owner.id))
51
- predicate2 = cpk_in_predicate(relation, Array(reflection.association_foreign_key), records.map { |x| x.id }) unless records == :all
52
- stmt = relation.where(predicate1.and(predicate2)).compile_delete
53
-
54
- owner.class.connection.delete stmt.to_sql
55
- end
56
- end
57
- end
58
- end
59
- end
@@ -1,39 +0,0 @@
1
- module ActiveRecord
2
- module Associations
3
- class JoinDependency
4
- class JoinPart
5
- def aliased_primary_key
6
- # CPK
7
- # "#{aliased_prefix}_r0"
8
-
9
- base_klass.composite? ?
10
- primary_key.inject([]) {|aliased_keys, key| aliased_keys << "#{ aliased_prefix }_r#{aliased_keys.length}"} :
11
- "#{ aliased_prefix }_r0"
12
- end
13
-
14
- def record_id(row)
15
- # CPK
16
- # row[aliased_primary_key]
17
- base_klass.composite? ?
18
- aliased_primary_key.map {|key| row[key]}.to_composite_keys :
19
- row[aliased_primary_key]
20
- end
21
-
22
- def column_names_with_alias
23
- unless @column_names_with_alias
24
- @column_names_with_alias = []
25
-
26
- # CPK
27
- #([primary_key] + (column_names - [primary_key])).each_with_index do |column_name, i|
28
- keys = base_klass.composite? ? primary_key.map(&:to_s) : [primary_key]
29
-
30
- (keys + (column_names - keys)).each_with_index do |column_name, i|
31
- @column_names_with_alias << [column_name, "#{aliased_prefix}_r#{i}"]
32
- end
33
- end
34
- @column_names_with_alias
35
- end
36
- end
37
- end
38
- end
39
- end