active_interaction 3.7.1 → 4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +190 -0
- data/CONTRIBUTING.md +1 -1
- data/README.md +96 -90
- data/lib/active_interaction.rb +1 -7
- data/lib/active_interaction/base.rb +48 -33
- data/lib/active_interaction/concerns/active_modelable.rb +1 -3
- data/lib/active_interaction/concerns/active_recordable.rb +1 -6
- data/lib/active_interaction/concerns/hashable.rb +0 -1
- data/lib/active_interaction/concerns/missable.rb +0 -1
- data/lib/active_interaction/concerns/runnable.rb +16 -28
- data/lib/active_interaction/errors.rb +8 -7
- data/lib/active_interaction/filter.rb +51 -37
- data/lib/active_interaction/filter_column.rb +0 -3
- data/lib/active_interaction/filters/abstract_date_time_filter.rb +34 -36
- data/lib/active_interaction/filters/abstract_numeric_filter.rb +27 -17
- data/lib/active_interaction/filters/array_filter.rb +57 -36
- data/lib/active_interaction/filters/boolean_filter.rb +26 -12
- data/lib/active_interaction/filters/date_filter.rb +1 -2
- data/lib/active_interaction/filters/date_time_filter.rb +1 -2
- data/lib/active_interaction/filters/decimal_filter.rb +10 -28
- data/lib/active_interaction/filters/file_filter.rb +6 -5
- data/lib/active_interaction/filters/float_filter.rb +1 -2
- data/lib/active_interaction/filters/hash_filter.rb +37 -27
- data/lib/active_interaction/filters/integer_filter.rb +7 -8
- data/lib/active_interaction/filters/interface_filter.rb +48 -14
- data/lib/active_interaction/filters/object_filter.rb +23 -50
- data/lib/active_interaction/filters/record_filter.rb +10 -35
- data/lib/active_interaction/filters/string_filter.rb +21 -12
- data/lib/active_interaction/filters/symbol_filter.rb +13 -7
- data/lib/active_interaction/filters/time_filter.rb +19 -22
- data/lib/active_interaction/grouped_input.rb +0 -3
- data/lib/active_interaction/inputs.rb +89 -0
- data/lib/active_interaction/locale/ja.yml +24 -0
- data/lib/active_interaction/modules/input_processor.rb +9 -6
- data/lib/active_interaction/modules/validation.rb +9 -12
- data/lib/active_interaction/version.rb +1 -3
- data/spec/active_interaction/base_spec.rb +95 -35
- data/spec/active_interaction/concerns/active_modelable_spec.rb +0 -2
- data/spec/active_interaction/concerns/active_recordable_spec.rb +0 -2
- data/spec/active_interaction/concerns/hashable_spec.rb +1 -3
- data/spec/active_interaction/concerns/missable_spec.rb +0 -2
- data/spec/active_interaction/concerns/runnable_spec.rb +32 -12
- data/spec/active_interaction/errors_spec.rb +49 -22
- data/spec/active_interaction/filter_column_spec.rb +0 -2
- data/spec/active_interaction/filter_spec.rb +0 -2
- data/spec/active_interaction/filters/abstract_date_time_filter_spec.rb +1 -3
- data/spec/active_interaction/filters/abstract_numeric_filter_spec.rb +1 -3
- data/spec/active_interaction/filters/array_filter_spec.rb +41 -15
- data/spec/active_interaction/filters/boolean_filter_spec.rb +58 -4
- data/spec/active_interaction/filters/date_filter_spec.rb +43 -3
- data/spec/active_interaction/filters/date_time_filter_spec.rb +43 -3
- data/spec/active_interaction/filters/decimal_filter_spec.rb +57 -3
- data/spec/active_interaction/filters/file_filter_spec.rb +1 -3
- data/spec/active_interaction/filters/float_filter_spec.rb +60 -4
- data/spec/active_interaction/filters/hash_filter_spec.rb +19 -9
- data/spec/active_interaction/filters/integer_filter_spec.rb +49 -7
- data/spec/active_interaction/filters/interface_filter_spec.rb +397 -24
- data/spec/active_interaction/filters/object_filter_spec.rb +23 -59
- data/spec/active_interaction/filters/record_filter_spec.rb +23 -49
- data/spec/active_interaction/filters/string_filter_spec.rb +15 -3
- data/spec/active_interaction/filters/symbol_filter_spec.rb +15 -3
- data/spec/active_interaction/filters/time_filter_spec.rb +65 -3
- data/spec/active_interaction/grouped_input_spec.rb +0 -2
- data/spec/active_interaction/i18n_spec.rb +3 -7
- data/spec/active_interaction/{modules/input_processor_spec.rb → inputs_spec.rb} +5 -5
- data/spec/active_interaction/integration/array_interaction_spec.rb +23 -12
- data/spec/active_interaction/integration/boolean_interaction_spec.rb +0 -2
- data/spec/active_interaction/integration/date_interaction_spec.rb +0 -2
- data/spec/active_interaction/integration/date_time_interaction_spec.rb +0 -2
- data/spec/active_interaction/integration/file_interaction_spec.rb +0 -2
- data/spec/active_interaction/integration/float_interaction_spec.rb +0 -2
- data/spec/active_interaction/integration/hash_interaction_spec.rb +0 -2
- data/spec/active_interaction/integration/integer_interaction_spec.rb +0 -2
- data/spec/active_interaction/integration/interface_interaction_spec.rb +1 -3
- data/spec/active_interaction/integration/object_interaction_spec.rb +0 -2
- data/spec/active_interaction/integration/string_interaction_spec.rb +0 -2
- data/spec/active_interaction/integration/symbol_interaction_spec.rb +0 -2
- data/spec/active_interaction/integration/time_interaction_spec.rb +14 -18
- data/spec/active_interaction/modules/validation_spec.rb +1 -3
- data/spec/spec_helper.rb +2 -6
- data/spec/support/concerns.rb +0 -2
- data/spec/support/filters.rb +13 -9
- data/spec/support/interactions.rb +22 -14
- metadata +106 -52
- data/lib/active_interaction/backports.rb +0 -59
- data/lib/active_interaction/filters/abstract_filter.rb +0 -19
- data/spec/active_interaction/filters/abstract_filter_spec.rb +0 -8
|
@@ -6,7 +6,7 @@ class RecordThing
|
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
def self.finder(_)
|
|
9
|
-
@
|
|
9
|
+
@finder ||= new
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def self.finds_nil(_)
|
|
@@ -17,11 +17,12 @@ class RecordThing
|
|
|
17
17
|
''
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
def self.passthrough(
|
|
21
|
-
|
|
20
|
+
def self.passthrough(obj)
|
|
21
|
+
obj
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
|
-
|
|
24
|
+
|
|
25
|
+
class RecordThings; end # rubocop:disable Lint/EmptyClass
|
|
25
26
|
BackupRecordThing = RecordThing
|
|
26
27
|
|
|
27
28
|
describe ActiveInteraction::RecordFilter, :filter do
|
|
@@ -38,78 +39,51 @@ describe ActiveInteraction::RecordFilter, :filter do
|
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
let(:value) { RecordThing.new }
|
|
41
|
-
let(:result) { filter.cast
|
|
42
|
+
let(:result) { filter.send(:cast, value, nil) }
|
|
42
43
|
|
|
43
|
-
context 'with
|
|
44
|
+
context 'with an instance of the class' do
|
|
44
45
|
it 'returns the instance' do
|
|
45
46
|
expect(result).to eql value
|
|
46
47
|
end
|
|
47
48
|
|
|
49
|
+
context 'with an instance that is a subclass' do
|
|
50
|
+
let(:subclass) { Class.new(RecordThing) }
|
|
51
|
+
let(:value) { subclass.new }
|
|
52
|
+
|
|
53
|
+
it 'returns the instance' do
|
|
54
|
+
expect(result).to eql value
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
48
58
|
it 'handles reconstantizing' do
|
|
49
59
|
expect(result).to eql value
|
|
50
60
|
|
|
51
61
|
Object.send(:remove_const, :RecordThing)
|
|
52
|
-
RecordThing = BackupRecordThing
|
|
62
|
+
RecordThing = BackupRecordThing # rubocop:disable Lint/ConstantDefinitionInBlock
|
|
53
63
|
value = RecordThing.new
|
|
54
64
|
|
|
55
|
-
expect(filter.cast
|
|
65
|
+
expect(filter.send(:cast, value, nil)).to eql value
|
|
56
66
|
end
|
|
57
67
|
|
|
58
68
|
it 'handles reconstantizing subclasses' do
|
|
59
69
|
filter
|
|
60
70
|
|
|
61
71
|
Object.send(:remove_const, :RecordThing)
|
|
62
|
-
RecordThing = BackupRecordThing
|
|
63
|
-
class SubRecordThing < RecordThing; end
|
|
72
|
+
RecordThing = BackupRecordThing # rubocop:disable Lint/ConstantDefinitionInBlock
|
|
73
|
+
class SubRecordThing < RecordThing; end # rubocop:disable Lint/ConstantDefinitionInBlock
|
|
64
74
|
value = SubRecordThing.new
|
|
65
75
|
|
|
66
|
-
expect(filter.cast
|
|
76
|
+
expect(filter.send(:cast, value, nil)).to eql value
|
|
67
77
|
end
|
|
68
78
|
|
|
69
79
|
context 'without the class available' do
|
|
70
80
|
before { Object.send(:remove_const, :RecordThing) }
|
|
71
|
-
after { RecordThing = BackupRecordThing }
|
|
81
|
+
after { RecordThing = BackupRecordThing } # rubocop:disable Lint/ConstantDefinitionInBlock
|
|
72
82
|
|
|
73
83
|
it 'does not raise an error on initialization' do
|
|
74
84
|
expect { filter }.to_not raise_error
|
|
75
85
|
end
|
|
76
86
|
end
|
|
77
|
-
|
|
78
|
-
context 'with bidirectional class comparisons' do
|
|
79
|
-
let(:case_equality) { false }
|
|
80
|
-
let(:class_equality) { false }
|
|
81
|
-
|
|
82
|
-
before do
|
|
83
|
-
options[:finder] = :passthrough
|
|
84
|
-
|
|
85
|
-
allow(RecordThing).to receive(:===).and_return(case_equality)
|
|
86
|
-
allow(value).to receive(:is_a?).and_return(class_equality)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
context 'without case or class equality' do
|
|
90
|
-
it 'raises an error' do
|
|
91
|
-
expect do
|
|
92
|
-
result
|
|
93
|
-
end.to raise_error ActiveInteraction::InvalidValueError
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
context 'with case equality' do
|
|
98
|
-
let(:case_equality) { true }
|
|
99
|
-
|
|
100
|
-
it 'returns the instance' do
|
|
101
|
-
expect(result).to eql value
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
context 'with class equality' do
|
|
106
|
-
let(:class_equality) { true }
|
|
107
|
-
|
|
108
|
-
it 'returns the instance' do
|
|
109
|
-
expect(result).to eql value
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
87
|
end
|
|
114
88
|
|
|
115
89
|
context 'with class as a superclass' do
|
|
@@ -150,7 +124,7 @@ describe ActiveInteraction::RecordFilter, :filter do
|
|
|
150
124
|
it 'raises an error' do
|
|
151
125
|
expect do
|
|
152
126
|
result
|
|
153
|
-
end.to raise_error ActiveInteraction::
|
|
127
|
+
end.to raise_error ActiveInteraction::InvalidNameError
|
|
154
128
|
end
|
|
155
129
|
end
|
|
156
130
|
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# coding: utf-8
|
|
2
|
-
|
|
3
1
|
require 'spec_helper'
|
|
4
2
|
|
|
5
3
|
describe ActiveInteraction::StringFilter, :filter do
|
|
@@ -13,7 +11,7 @@ describe ActiveInteraction::StringFilter, :filter do
|
|
|
13
11
|
end
|
|
14
12
|
|
|
15
13
|
describe '#cast' do
|
|
16
|
-
let(:result) { filter.cast
|
|
14
|
+
let(:result) { filter.send(:cast, value, nil) }
|
|
17
15
|
|
|
18
16
|
context 'with a String' do
|
|
19
17
|
let(:value) { SecureRandom.hex }
|
|
@@ -23,6 +21,20 @@ describe ActiveInteraction::StringFilter, :filter do
|
|
|
23
21
|
end
|
|
24
22
|
end
|
|
25
23
|
|
|
24
|
+
context 'with an implicit String' do
|
|
25
|
+
let(:value) do
|
|
26
|
+
Class.new do
|
|
27
|
+
def to_str
|
|
28
|
+
@to_str ||= SecureRandom.hex
|
|
29
|
+
end
|
|
30
|
+
end.new
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'returns the String' do
|
|
34
|
+
expect(result).to eql value.to_str
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
26
38
|
context 'with a strippable String' do
|
|
27
39
|
let(:value) { " #{SecureRandom.hex} " }
|
|
28
40
|
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# coding: utf-8
|
|
2
|
-
|
|
3
1
|
require 'spec_helper'
|
|
4
2
|
|
|
5
3
|
describe ActiveInteraction::SymbolFilter, :filter do
|
|
@@ -7,7 +5,7 @@ describe ActiveInteraction::SymbolFilter, :filter do
|
|
|
7
5
|
it_behaves_like 'a filter'
|
|
8
6
|
|
|
9
7
|
describe '#cast' do
|
|
10
|
-
let(:result) { filter.cast
|
|
8
|
+
let(:result) { filter.send(:cast, value, nil) }
|
|
11
9
|
|
|
12
10
|
context 'with a Symbol' do
|
|
13
11
|
let(:value) { SecureRandom.hex.to_sym }
|
|
@@ -17,6 +15,20 @@ describe ActiveInteraction::SymbolFilter, :filter do
|
|
|
17
15
|
end
|
|
18
16
|
end
|
|
19
17
|
|
|
18
|
+
context 'with an implicit Symbol' do
|
|
19
|
+
let(:value) do
|
|
20
|
+
Class.new do
|
|
21
|
+
def to_sym
|
|
22
|
+
:symbol
|
|
23
|
+
end
|
|
24
|
+
end.new
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'returns a symbol' do
|
|
28
|
+
expect(result).to eql value.to_sym
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
20
32
|
context 'with a String' do
|
|
21
33
|
let(:value) { SecureRandom.hex }
|
|
22
34
|
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# coding: utf-8
|
|
2
|
-
|
|
3
1
|
require 'spec_helper'
|
|
4
2
|
|
|
5
3
|
describe ActiveInteraction::TimeFilter, :filter do
|
|
@@ -37,7 +35,7 @@ describe ActiveInteraction::TimeFilter, :filter do
|
|
|
37
35
|
end
|
|
38
36
|
|
|
39
37
|
describe '#cast' do
|
|
40
|
-
let(:result) { filter.cast
|
|
38
|
+
let(:result) { filter.send(:cast, value, nil) }
|
|
41
39
|
|
|
42
40
|
context 'with a Time' do
|
|
43
41
|
let(:value) { Time.new }
|
|
@@ -85,6 +83,70 @@ describe ActiveInteraction::TimeFilter, :filter do
|
|
|
85
83
|
end
|
|
86
84
|
end
|
|
87
85
|
|
|
86
|
+
context 'with an implicit String' do
|
|
87
|
+
let(:value) do
|
|
88
|
+
Class.new do
|
|
89
|
+
def to_str
|
|
90
|
+
'2011-12-13 14:15:16 +1718'
|
|
91
|
+
end
|
|
92
|
+
end.new
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it 'returns a Time' do
|
|
96
|
+
expect(result).to eql Time.parse(value)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
context 'with a blank String' do
|
|
101
|
+
let(:value) do
|
|
102
|
+
Class.new do
|
|
103
|
+
def to_str
|
|
104
|
+
' '
|
|
105
|
+
end
|
|
106
|
+
end.new
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
context 'optional' do
|
|
110
|
+
include_context 'optional'
|
|
111
|
+
|
|
112
|
+
it 'returns the default' do
|
|
113
|
+
expect(result).to eql options[:default]
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
context 'required' do
|
|
118
|
+
include_context 'required'
|
|
119
|
+
|
|
120
|
+
it 'raises an error' do
|
|
121
|
+
expect do
|
|
122
|
+
result
|
|
123
|
+
end.to raise_error ActiveInteraction::MissingValueError
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
context 'with an Integer' do
|
|
129
|
+
let(:value) { rand(1 << 16) }
|
|
130
|
+
|
|
131
|
+
it 'returns the Time' do
|
|
132
|
+
expect(result).to eql Time.at(value)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
context 'with an implicit Integer' do
|
|
137
|
+
let(:value) do
|
|
138
|
+
Class.new do
|
|
139
|
+
def to_int
|
|
140
|
+
@to_int ||= rand(1 << 16)
|
|
141
|
+
end
|
|
142
|
+
end.new
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it 'returns the Time' do
|
|
146
|
+
expect(result).to eql Time.at(value)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
88
150
|
context 'with a GroupedInput' do
|
|
89
151
|
let(:year) { 2012 }
|
|
90
152
|
let(:month) { 1 }
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# coding: utf-8
|
|
2
|
-
|
|
3
1
|
require 'spec_helper'
|
|
4
2
|
|
|
5
3
|
describe ActiveInteraction do
|
|
@@ -20,7 +18,7 @@ end
|
|
|
20
18
|
describe I18nInteraction do
|
|
21
19
|
include_context 'interactions'
|
|
22
20
|
|
|
23
|
-
TYPES = ActiveInteraction::Filter
|
|
21
|
+
TYPES = ActiveInteraction::Filter # rubocop:disable Lint/ConstantDefinitionInBlock
|
|
24
22
|
.const_get(:CLASSES)
|
|
25
23
|
.map { |slug, _| slug.to_s }
|
|
26
24
|
|
|
@@ -87,9 +85,7 @@ describe I18nInteraction do
|
|
|
87
85
|
# This must appear before including the translation examples so that the
|
|
88
86
|
# locale is available before it is assigned.
|
|
89
87
|
locale = :hsilgne
|
|
90
|
-
unless I18n.locale_available?(locale)
|
|
91
|
-
I18n.config.available_locales = I18n.config.available_locales + [locale]
|
|
92
|
-
end
|
|
88
|
+
I18n.config.available_locales = I18n.config.available_locales + [locale] unless I18n.locale_available?(locale)
|
|
93
89
|
end
|
|
94
90
|
|
|
95
91
|
include_examples 'translation', :hsilgne
|
|
@@ -100,7 +96,7 @@ describe I18nInteraction do
|
|
|
100
96
|
errors: {
|
|
101
97
|
messages: {
|
|
102
98
|
invalid: 'is invalid'.reverse,
|
|
103
|
-
invalid_type: "
|
|
99
|
+
invalid_type: "%<type>s} #{'is not a valid'.reverse}",
|
|
104
100
|
missing: 'missing'.reverse
|
|
105
101
|
}
|
|
106
102
|
},
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# coding: utf-8
|
|
2
|
-
|
|
3
1
|
require 'spec_helper'
|
|
4
2
|
|
|
5
|
-
describe ActiveInteraction::
|
|
3
|
+
describe ActiveInteraction::Inputs do
|
|
4
|
+
subject(:inputs) { described_class.new }
|
|
5
|
+
|
|
6
6
|
describe '.reserved?(name)' do
|
|
7
7
|
it 'returns true for anything starting with "_interaction_"' do
|
|
8
8
|
expect(described_class.reserved?('_interaction_')).to be_truthy
|
|
@@ -10,8 +10,8 @@ describe ActiveInteraction::InputProcessor do
|
|
|
10
10
|
|
|
11
11
|
it 'returns true for existing instance methods' do
|
|
12
12
|
(
|
|
13
|
-
ActiveInteraction::Base.instance_methods +
|
|
14
|
-
ActiveInteraction::Base.private_instance_methods
|
|
13
|
+
(ActiveInteraction::Base.instance_methods - Object.instance_methods) +
|
|
14
|
+
(ActiveInteraction::Base.private_instance_methods - Object.private_instance_methods)
|
|
15
15
|
).each do |method|
|
|
16
16
|
expect(described_class.reserved?(method)).to be_truthy
|
|
17
17
|
end
|
|
@@ -1,15 +1,27 @@
|
|
|
1
|
-
# coding: utf-8
|
|
2
|
-
|
|
3
1
|
require 'spec_helper'
|
|
2
|
+
require 'active_record'
|
|
3
|
+
if defined?(JRUBY_VERSION)
|
|
4
|
+
require 'activerecord-jdbcsqlite3-adapter'
|
|
5
|
+
else
|
|
6
|
+
require 'sqlite3'
|
|
7
|
+
end
|
|
4
8
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
ActiveRecord::Base.establish_connection(
|
|
10
|
+
adapter: 'sqlite3',
|
|
11
|
+
database: ':memory:'
|
|
12
|
+
)
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
ActiveRecord::Schema.define do
|
|
15
|
+
create_table(:lists)
|
|
16
|
+
create_table(:elements) { |t| t.column(:list_id, :integer) }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class List < ActiveRecord::Base
|
|
20
|
+
has_many :elements
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class Element < ActiveRecord::Base
|
|
24
|
+
belongs_to :list
|
|
13
25
|
end
|
|
14
26
|
|
|
15
27
|
ArrayInteraction = Class.new(TestInteraction) do
|
|
@@ -24,9 +36,8 @@ end
|
|
|
24
36
|
describe ArrayInteraction do
|
|
25
37
|
include_context 'interactions'
|
|
26
38
|
it_behaves_like 'an interaction', :array, -> { [] }
|
|
27
|
-
it_behaves_like 'an interaction', :array, -> {
|
|
28
|
-
it_behaves_like 'an interaction', :array,
|
|
29
|
-
-> { ActiveRecord::Associations::CollectionProxy.new }
|
|
39
|
+
it_behaves_like 'an interaction', :array, -> { Element.where('1 = 1') }
|
|
40
|
+
it_behaves_like 'an interaction', :array, -> { List.create!.elements }
|
|
30
41
|
|
|
31
42
|
context 'with inputs[:a]' do
|
|
32
43
|
let(:a) { [[]] }
|