multiple_table_inheritance 0.1.3 → 0.1.4
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.
- data/.travis.yml +7 -0
- data/CHANGELOG.md +7 -0
- data/README.md +49 -13
- data/lib/multiple_table_inheritance/child.rb +1 -0
- data/lib/multiple_table_inheritance/child/base.rb +15 -12
- data/lib/multiple_table_inheritance/child/sanitizer.rb +57 -0
- data/lib/multiple_table_inheritance/parent/base.rb +1 -1
- data/lib/multiple_table_inheritance/parent/relation.rb +1 -1
- data/lib/multiple_table_inheritance/version.rb +1 -1
- data/{multiiple_table_inheritance.gemspec → multiple_table_inheritance.gemspec} +0 -0
- data/spec/active_record/child_spec.rb +383 -56
- data/spec/active_record/parent_spec.rb +51 -37
- data/spec/spec_helper.rb +10 -5
- data/spec/support/models.rb +75 -9
- data/spec/support/tables.rb +59 -10
- metadata +17 -15
@@ -10,58 +10,57 @@ describe MultipleTableInheritance::Parent do
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
context 'non-namespaced classes' do
|
13
|
+
context 'non-namespaced classes with default subtype' do
|
14
14
|
context 'retrieving records' do
|
15
15
|
before do
|
16
16
|
mock_employees!
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
it 'should retrieve child records' do
|
20
|
-
Employee.find_each do |
|
21
|
-
|
22
|
-
['Programmer', 'Manager'].should include(
|
20
|
+
Employee.find_each do |employee_subtype|
|
21
|
+
employee_subtype.should_not be_instance_of(Employee)
|
22
|
+
['Programmer', 'Manager', 'Janitor'].should include(employee_subtype.class.to_s)
|
23
23
|
end
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
it 'should allow access to parent record' do
|
27
|
-
|
28
|
-
|
27
|
+
employee_subtype = Employee.first
|
28
|
+
employee_subtype.employee.should be_instance_of(Employee)
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
it 'should include all records' do
|
32
32
|
modified_results = Employee.all
|
33
33
|
original_results = Employee.as_supertype.all
|
34
34
|
modified_results.size.should == original_results.size
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
37
|
it 'should maintain result order' do
|
38
38
|
modified_results = Employee.order("id desc").all
|
39
39
|
original_results = Employee.as_supertype.order("id desc").all
|
40
40
|
modified_results.collect(&:id).should == original_results.collect(&:id)
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
context 'associations preloading' do
|
44
44
|
context 'is enabled' do
|
45
45
|
before do
|
46
|
-
@
|
46
|
+
@employee_subtype = Employee.includes(:team).first
|
47
47
|
end
|
48
|
-
|
48
|
+
|
49
49
|
it 'should not perform an extra find' do
|
50
|
-
|
51
|
-
|
52
|
-
@programmer_or_manager.employee.team
|
50
|
+
Team.should_not_receive(:find_by_sql)
|
51
|
+
@employee_subtype.employee.team
|
53
52
|
end
|
54
53
|
end
|
55
|
-
|
54
|
+
|
56
55
|
context 'is disabled' do
|
57
56
|
before do
|
58
|
-
@
|
57
|
+
@employee_subtype = Employee.first
|
59
58
|
end
|
60
|
-
|
59
|
+
|
61
60
|
it 'should not perform an extra find' do
|
62
|
-
pending "
|
63
|
-
Team.
|
64
|
-
@
|
61
|
+
pending "appears to be an rspec bug preventing this from working"
|
62
|
+
Team.should_not_receive(:find_by_sql).with(any_args).once
|
63
|
+
@employee_subtype.employee.team
|
65
64
|
end
|
66
65
|
end
|
67
66
|
end
|
@@ -69,7 +68,7 @@ describe MultipleTableInheritance::Parent do
|
|
69
68
|
|
70
69
|
context 'deleting records' do
|
71
70
|
before do
|
72
|
-
programmer = Programmer.create!(:first_name => 'Billy', :last_name => 'Ray', :salary => 50000
|
71
|
+
programmer = Programmer.create!(:first_name => 'Billy', :last_name => 'Ray', :salary => 50000)
|
73
72
|
@employee = programmer.employee
|
74
73
|
@employee_id = programmer.id
|
75
74
|
end
|
@@ -87,30 +86,45 @@ describe MultipleTableInheritance::Parent do
|
|
87
86
|
|
88
87
|
context 'an invalid subtype exists' do
|
89
88
|
before do
|
90
|
-
@employee = Employee.create!(:first_name => 'Sub', :last_name => 'Type', :salary => 50000
|
89
|
+
@employee = Employee.create!(:first_name => 'Sub', :last_name => 'Type', :salary => 50000) do |employee|
|
91
90
|
employee.subtype = 'DoesNotExist'
|
92
91
|
end
|
93
92
|
end
|
94
93
|
|
95
|
-
it 'should
|
96
|
-
@employee.should
|
94
|
+
it 'should not have errors' do
|
95
|
+
@employee.errors.messages.should be_empty
|
97
96
|
end
|
98
97
|
|
99
|
-
it 'should
|
100
|
-
|
98
|
+
it 'should have been saved' do
|
99
|
+
@employee.should_not be_new_record
|
100
|
+
end
|
101
|
+
|
102
|
+
context 'retrieving saved record' do
|
103
|
+
before do
|
104
|
+
@record = Employee.find_by_id(@employee.id)
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'should not return the parent model instance' do
|
108
|
+
@record.should be_nil
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'logger exists on model' do
|
112
|
+
before do
|
113
|
+
require 'logger'
|
114
|
+
@logger = Logger.new(nil)
|
115
|
+
ActiveRecord::Base.stub(:logger).and_return(@logger)
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'should log a warning' do
|
119
|
+
@logger.should_receive(:warn)
|
120
|
+
Employee.find_by_id(@employee.id)
|
121
|
+
end
|
122
|
+
end
|
101
123
|
end
|
102
|
-
end
|
103
|
-
|
104
|
-
context 'default subtype' do
|
105
|
-
pending "test_everything"
|
106
|
-
end
|
107
|
-
|
108
|
-
context 'custom subtype' do
|
109
|
-
pending "test_everything"
|
110
124
|
end
|
111
125
|
end
|
112
126
|
|
113
|
-
context 'namespaced classes' do
|
127
|
+
context 'namespaced classes with custom subtype' do
|
114
128
|
context 'retrieving records' do
|
115
129
|
before(:each) do
|
116
130
|
mock_pets!
|
data/spec/spec_helper.rb
CHANGED
@@ -10,7 +10,7 @@ require 'support/models'
|
|
10
10
|
module MultipleTableInheritanceSpecHelper
|
11
11
|
def mock_employees!
|
12
12
|
3.times do |i|
|
13
|
-
team = Team.create!(:name => "Team#{i}")
|
13
|
+
team = Team.create!(:name => "Team #{i}")
|
14
14
|
language = Language.create!(:name => "Java 1.#{i + 4}")
|
15
15
|
end
|
16
16
|
|
@@ -28,6 +28,13 @@ module MultipleTableInheritanceSpecHelper
|
|
28
28
|
:salary => 70000 + (i * 2500),
|
29
29
|
:team => Team.first,
|
30
30
|
:bonus => i * 2500) # manager-specific field
|
31
|
+
|
32
|
+
Janitor.create!(
|
33
|
+
:first_name => "Phil",
|
34
|
+
:last_name => "Moore #{i}",
|
35
|
+
:salary => 40000 + (i * 1000),
|
36
|
+
:team => Team.last,
|
37
|
+
:preferred_cleaner => %w{Comet Windex Swiffer}[i % 3]) # janitor-specific field
|
31
38
|
end
|
32
39
|
end
|
33
40
|
|
@@ -39,15 +46,13 @@ module MultipleTableInheritanceSpecHelper
|
|
39
46
|
|
40
47
|
dog = Pet::Dog.create!(:name => "Rover #{i}") do |dog|
|
41
48
|
dog.owner = owner
|
42
|
-
dog.favorite_toy = "#{i + 1}-inch Bone"
|
49
|
+
dog.favorite_toy = "#{i + 1}-inch Bone" # dog-specific field
|
43
50
|
end
|
44
|
-
puts dog.inspect
|
45
51
|
|
46
52
|
cat = Pet::Cat.create!(:name => "Mittens #{i}") do |cat|
|
47
53
|
cat.owner = owner
|
48
|
-
cat.longest_nap = 100 + i
|
54
|
+
cat.longest_nap = 100 + i # cat-specific field
|
49
55
|
end
|
50
|
-
puts cat.inspect
|
51
56
|
end
|
52
57
|
end
|
53
58
|
end
|
data/spec/support/models.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
|
2
|
-
# Non-namespaced models
|
3
|
-
|
1
|
+
#########################################################
|
2
|
+
# Non-namespaced models with mass-assignment security
|
3
|
+
# everywhere or only on parent.
|
4
|
+
#########################################################
|
4
5
|
|
5
6
|
class Employee < ActiveRecord::Base
|
6
7
|
acts_as_superclass
|
@@ -9,21 +10,29 @@ class Employee < ActiveRecord::Base
|
|
9
10
|
validates :first_name, :presence => true
|
10
11
|
validates :last_name, :presence => true
|
11
12
|
validates :salary, :presence => true, :numericality => { :min => 0 }
|
13
|
+
|
14
|
+
def give_raise!(amount)
|
15
|
+
Employee.update_counters self.id, :salary => amount
|
16
|
+
end
|
12
17
|
end
|
13
18
|
|
14
19
|
class Programmer < ActiveRecord::Base
|
15
|
-
inherits_from :employee
|
20
|
+
inherits_from :employee #, :methods => true
|
16
21
|
attr_accessible :languages, :language_ids
|
17
22
|
has_many :known_languages
|
18
23
|
has_many :languages, :through => :known_languages
|
19
24
|
end
|
20
25
|
|
21
26
|
class Manager < ActiveRecord::Base
|
22
|
-
inherits_from :employee
|
27
|
+
inherits_from :employee #, :methods => true
|
23
28
|
attr_accessible :bonus
|
24
29
|
validates :bonus, :numericality => true
|
25
30
|
end
|
26
31
|
|
32
|
+
class Janitor < ActiveRecord::Base
|
33
|
+
inherits_from :employee #, :methods => true
|
34
|
+
end
|
35
|
+
|
27
36
|
class Team < ActiveRecord::Base
|
28
37
|
attr_accessible :name, :description
|
29
38
|
has_many :employees
|
@@ -44,9 +53,32 @@ class KnownLanguage < ActiveRecord::Base
|
|
44
53
|
validates :language_id, :presence => true, :uniqueness => { :scope => :programmer_id }
|
45
54
|
end
|
46
55
|
|
47
|
-
|
48
|
-
#
|
49
|
-
|
56
|
+
#########################################################
|
57
|
+
# Non-namespaced models with mass assignment security
|
58
|
+
# only on children or not specified.
|
59
|
+
#########################################################
|
60
|
+
|
61
|
+
class Clothing < ActiveRecord::Base
|
62
|
+
acts_as_superclass
|
63
|
+
validates :color, :presence => true
|
64
|
+
end
|
65
|
+
|
66
|
+
class Shirt < ActiveRecord::Base
|
67
|
+
inherits_from :clothing
|
68
|
+
attr_accessible :size
|
69
|
+
validates :size, :presence => true
|
70
|
+
end
|
71
|
+
|
72
|
+
class Pants < ActiveRecord::Base
|
73
|
+
inherits_from :clothing
|
74
|
+
validates :waist_size, :presence => true
|
75
|
+
validates :length, :presence => true
|
76
|
+
end
|
77
|
+
|
78
|
+
#########################################################
|
79
|
+
# Namespaced models with mass assignment security
|
80
|
+
# everywhere or only on parent.
|
81
|
+
#########################################################
|
50
82
|
|
51
83
|
module Pet
|
52
84
|
def self.table_name_prefix
|
@@ -67,13 +99,47 @@ module Pet
|
|
67
99
|
belongs_to :owner
|
68
100
|
validates :owner_id, :presence => true
|
69
101
|
validates :name, :presence => true
|
102
|
+
|
103
|
+
def rename!(new_name)
|
104
|
+
update_attributes!(:name => new_name)
|
105
|
+
end
|
70
106
|
end
|
71
107
|
|
72
|
-
class
|
108
|
+
class Cat < ActiveRecord::Base
|
73
109
|
inherits_from :pet, :class_name => 'Pet::Pet'
|
110
|
+
attr_accessible :longest_nap
|
74
111
|
end
|
75
112
|
|
76
113
|
class Dog < ActiveRecord::Base
|
77
114
|
inherits_from :pet, :class_name => 'Pet::Pet'
|
78
115
|
end
|
79
116
|
end
|
117
|
+
|
118
|
+
#########################################################
|
119
|
+
# Namespaced models with mass assignment security
|
120
|
+
# only on children or not specified.
|
121
|
+
#########################################################
|
122
|
+
|
123
|
+
module Store
|
124
|
+
def self.table_name_prefix
|
125
|
+
'store_'
|
126
|
+
end
|
127
|
+
|
128
|
+
class Furniture < ActiveRecord::Base
|
129
|
+
acts_as_superclass
|
130
|
+
validates :brand, :presence => true
|
131
|
+
validates :name, :presence => true
|
132
|
+
end
|
133
|
+
|
134
|
+
class Bed < ActiveRecord::Base
|
135
|
+
inherits_from :furniture, :class_name => 'Store::Furniture'
|
136
|
+
validates :size, :presence => true
|
137
|
+
end
|
138
|
+
|
139
|
+
class Table < ActiveRecord::Base
|
140
|
+
inherits_from :furniture, :class_name => 'Store::Furniture'
|
141
|
+
attr_accessible :chairs, :color
|
142
|
+
validates :chairs, :presence => true, :numericality => { :min => 1 }
|
143
|
+
validates :color, :presence => true
|
144
|
+
end
|
145
|
+
end
|
data/spec/support/tables.rb
CHANGED
@@ -1,14 +1,18 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
:database => File.expand_path(File.join(File.dirname(__FILE__), '../../db/multiple_table_inheritance.db'))
|
4
|
-
)
|
1
|
+
db_path = File.expand_path(File.join(File.dirname(__FILE__), '../../db'))
|
2
|
+
db_file = File.join(db_path, 'multiple_table_inheritance.db')
|
5
3
|
|
4
|
+
# Create/Overwrite database file
|
5
|
+
File.delete(db_file) if File.exist?(db_file)
|
6
|
+
Dir.mkdir(db_path) unless File.directory?(db_path)
|
7
|
+
File.open(db_file, 'w') {}
|
8
|
+
|
9
|
+
# Open a database connection
|
10
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => db_file)
|
6
11
|
conn = ActiveRecord::Base.connection
|
7
12
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
13
|
+
#########################################################
|
14
|
+
# Employee entities
|
15
|
+
#########################################################
|
12
16
|
|
13
17
|
conn.create_table :employees do |t|
|
14
18
|
t.string :subtype, :null => false
|
@@ -25,6 +29,10 @@ conn.create_table :managers, :inherits => :employee do |t|
|
|
25
29
|
t.integer :bonus
|
26
30
|
end
|
27
31
|
|
32
|
+
conn.create_table :janitors, :inherits => :employee do |t|
|
33
|
+
t.string :preferred_cleaner
|
34
|
+
end
|
35
|
+
|
28
36
|
conn.create_table :teams do |t|
|
29
37
|
t.string :name, :null => false
|
30
38
|
end
|
@@ -38,6 +46,28 @@ conn.create_table :known_languages do |t|
|
|
38
46
|
t.integer :language_id, :null => false
|
39
47
|
end
|
40
48
|
|
49
|
+
#########################################################
|
50
|
+
# Clothing entities
|
51
|
+
#########################################################
|
52
|
+
|
53
|
+
conn.create_table :clothings do |t|
|
54
|
+
t.string :subtype, :null => false
|
55
|
+
t.string :color, :null => false
|
56
|
+
end
|
57
|
+
|
58
|
+
conn.create_table :shirts, :inherits => :clothing do |t|
|
59
|
+
t.string :size, :limit => 2, :null => false
|
60
|
+
end
|
61
|
+
|
62
|
+
conn.create_table :pants, :inherits => :clothing do |t|
|
63
|
+
t.integer :waist_size, :null => false
|
64
|
+
t.integer :length, :null => false
|
65
|
+
end
|
66
|
+
|
67
|
+
#########################################################
|
68
|
+
# Pet entities
|
69
|
+
#########################################################
|
70
|
+
|
41
71
|
conn.create_table :pet_owners do |t|
|
42
72
|
t.string :first_name, :null => false
|
43
73
|
t.string :last_name, :null => false
|
@@ -51,10 +81,29 @@ conn.create_table :pet_pets do |t|
|
|
51
81
|
t.string :color
|
52
82
|
end
|
53
83
|
|
54
|
-
conn.create_table :pet_dogs, :inherits => :
|
84
|
+
conn.create_table :pet_dogs, :inherits => :pet do |t|
|
55
85
|
t.string :favorite_toy
|
56
86
|
end
|
57
87
|
|
58
|
-
conn.create_table :pet_cats, :inherits => :
|
88
|
+
conn.create_table :pet_cats, :inherits => :pet do |t|
|
59
89
|
t.integer :longest_nap
|
60
90
|
end
|
91
|
+
|
92
|
+
#########################################################
|
93
|
+
# Store entities
|
94
|
+
#########################################################
|
95
|
+
|
96
|
+
conn.create_table :store_furnitures do |t|
|
97
|
+
t.string :subtype, :null => false
|
98
|
+
t.string :brand, :null => false
|
99
|
+
t.string :name, :null => false
|
100
|
+
end
|
101
|
+
|
102
|
+
conn.create_table :store_beds, :inherits => :furniture do |t|
|
103
|
+
t.string :size, :null => false
|
104
|
+
end
|
105
|
+
|
106
|
+
conn.create_table :store_tables, :inherits => :furniture do |t|
|
107
|
+
t.integer :chairs, :null => false
|
108
|
+
t.string :color, :null => false
|
109
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: multiple_table_inheritance
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-03-
|
12
|
+
date: 2012-03-13 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
16
|
-
requirement: &
|
16
|
+
requirement: &2153195660 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 3.0.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *2153195660
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: activesupport
|
27
|
-
requirement: &
|
27
|
+
requirement: &2153195200 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 3.0.0
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *2153195200
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rspec-rails
|
38
|
-
requirement: &
|
38
|
+
requirement: &2153194720 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 2.8.0
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *2153194720
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rspec_tag_matchers
|
49
|
-
requirement: &
|
49
|
+
requirement: &2153194240 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 1.0.0
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *2153194240
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: sqlite3-ruby
|
60
|
-
requirement: &
|
60
|
+
requirement: &2153193740 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: 1.3.3
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *2153193740
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: database_cleaner
|
71
|
-
requirement: &
|
71
|
+
requirement: &2153142060 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ! '>='
|
@@ -76,7 +76,7 @@ dependencies:
|
|
76
76
|
version: 0.7.1
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *2153142060
|
80
80
|
description: ActiveRecord plugin designed to allow simple multiple table inheritance.
|
81
81
|
email:
|
82
82
|
- matt@matthuggins.com
|
@@ -87,6 +87,7 @@ extra_rdoc_files:
|
|
87
87
|
files:
|
88
88
|
- .gitignore
|
89
89
|
- .rspec
|
90
|
+
- .travis.yml
|
90
91
|
- CHANGELOG.md
|
91
92
|
- Gemfile
|
92
93
|
- README.md
|
@@ -94,13 +95,14 @@ files:
|
|
94
95
|
- lib/multiple_table_inheritance.rb
|
95
96
|
- lib/multiple_table_inheritance/child.rb
|
96
97
|
- lib/multiple_table_inheritance/child/base.rb
|
98
|
+
- lib/multiple_table_inheritance/child/sanitizer.rb
|
97
99
|
- lib/multiple_table_inheritance/migration.rb
|
98
100
|
- lib/multiple_table_inheritance/parent.rb
|
99
101
|
- lib/multiple_table_inheritance/parent/base.rb
|
100
102
|
- lib/multiple_table_inheritance/parent/relation.rb
|
101
103
|
- lib/multiple_table_inheritance/railtie.rb
|
102
104
|
- lib/multiple_table_inheritance/version.rb
|
103
|
-
-
|
105
|
+
- multiple_table_inheritance.gemspec
|
104
106
|
- spec/active_record/child_spec.rb
|
105
107
|
- spec/active_record/parent_spec.rb
|
106
108
|
- spec/spec_helper.rb
|