arel 0.1.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.
- data/.gitignore +6 -0
- data/README.markdown +184 -0
- data/Rakefile +60 -0
- data/VERSION +1 -0
- data/arel.gemspec +233 -0
- data/doc/CONVENTIONS +17 -0
- data/doc/TODO +118 -0
- data/lib/arel.rb +10 -0
- data/lib/arel/algebra.rb +4 -0
- data/lib/arel/algebra/extensions.rb +4 -0
- data/lib/arel/algebra/extensions/class.rb +32 -0
- data/lib/arel/algebra/extensions/hash.rb +11 -0
- data/lib/arel/algebra/extensions/object.rb +17 -0
- data/lib/arel/algebra/extensions/symbol.rb +9 -0
- data/lib/arel/algebra/predicates.rb +41 -0
- data/lib/arel/algebra/primitives.rb +5 -0
- data/lib/arel/algebra/primitives/attribute.rb +150 -0
- data/lib/arel/algebra/primitives/expression.rb +43 -0
- data/lib/arel/algebra/primitives/ordering.rb +23 -0
- data/lib/arel/algebra/primitives/value.rb +14 -0
- data/lib/arel/algebra/relations.rb +14 -0
- data/lib/arel/algebra/relations/operations/alias.rb +7 -0
- data/lib/arel/algebra/relations/operations/group.rb +12 -0
- data/lib/arel/algebra/relations/operations/join.rb +64 -0
- data/lib/arel/algebra/relations/operations/order.rb +18 -0
- data/lib/arel/algebra/relations/operations/project.rb +20 -0
- data/lib/arel/algebra/relations/operations/skip.rb +6 -0
- data/lib/arel/algebra/relations/operations/take.rb +10 -0
- data/lib/arel/algebra/relations/operations/where.rb +16 -0
- data/lib/arel/algebra/relations/relation.rb +136 -0
- data/lib/arel/algebra/relations/row.rb +26 -0
- data/lib/arel/algebra/relations/utilities/compound.rb +30 -0
- data/lib/arel/algebra/relations/utilities/externalization.rb +24 -0
- data/lib/arel/algebra/relations/utilities/nil.rb +7 -0
- data/lib/arel/algebra/relations/writes.rb +36 -0
- data/lib/arel/engines.rb +2 -0
- data/lib/arel/engines/memory.rb +4 -0
- data/lib/arel/engines/memory/engine.rb +16 -0
- data/lib/arel/engines/memory/predicates.rb +35 -0
- data/lib/arel/engines/memory/primitives.rb +27 -0
- data/lib/arel/engines/memory/relations.rb +5 -0
- data/lib/arel/engines/memory/relations/array.rb +25 -0
- data/lib/arel/engines/memory/relations/compound.rb +9 -0
- data/lib/arel/engines/memory/relations/operations.rb +61 -0
- data/lib/arel/engines/memory/relations/writes.rb +7 -0
- data/lib/arel/engines/sql.rb +7 -0
- data/lib/arel/engines/sql/christener.rb +13 -0
- data/lib/arel/engines/sql/engine.rb +37 -0
- data/lib/arel/engines/sql/extensions.rb +4 -0
- data/lib/arel/engines/sql/extensions/array.rb +16 -0
- data/lib/arel/engines/sql/extensions/nil_class.rb +11 -0
- data/lib/arel/engines/sql/extensions/object.rb +15 -0
- data/lib/arel/engines/sql/extensions/range.rb +15 -0
- data/lib/arel/engines/sql/formatters.rb +113 -0
- data/lib/arel/engines/sql/predicates.rb +51 -0
- data/lib/arel/engines/sql/primitives.rb +85 -0
- data/lib/arel/engines/sql/relations.rb +9 -0
- data/lib/arel/engines/sql/relations/operations/alias.rb +5 -0
- data/lib/arel/engines/sql/relations/operations/join.rb +33 -0
- data/lib/arel/engines/sql/relations/relation.rb +50 -0
- data/lib/arel/engines/sql/relations/table.rb +52 -0
- data/lib/arel/engines/sql/relations/utilities/compound.rb +10 -0
- data/lib/arel/engines/sql/relations/utilities/externalization.rb +14 -0
- data/lib/arel/engines/sql/relations/utilities/nil.rb +6 -0
- data/lib/arel/engines/sql/relations/utilities/recursion.rb +13 -0
- data/lib/arel/engines/sql/relations/writes.rb +39 -0
- data/lib/arel/session.rb +48 -0
- data/spec/arel/algebra/unit/predicates/binary_spec.rb +33 -0
- data/spec/arel/algebra/unit/predicates/equality_spec.rb +27 -0
- data/spec/arel/algebra/unit/predicates/in_spec.rb +10 -0
- data/spec/arel/algebra/unit/primitives/attribute_spec.rb +183 -0
- data/spec/arel/algebra/unit/primitives/expression_spec.rb +45 -0
- data/spec/arel/algebra/unit/primitives/value_spec.rb +15 -0
- data/spec/arel/algebra/unit/relations/alias_spec.rb +16 -0
- data/spec/arel/algebra/unit/relations/delete_spec.rb +9 -0
- data/spec/arel/algebra/unit/relations/group_spec.rb +10 -0
- data/spec/arel/algebra/unit/relations/insert_spec.rb +9 -0
- data/spec/arel/algebra/unit/relations/join_spec.rb +26 -0
- data/spec/arel/algebra/unit/relations/order_spec.rb +21 -0
- data/spec/arel/algebra/unit/relations/project_spec.rb +34 -0
- data/spec/arel/algebra/unit/relations/relation_spec.rb +188 -0
- data/spec/arel/algebra/unit/relations/skip_spec.rb +10 -0
- data/spec/arel/algebra/unit/relations/table_spec.rb +39 -0
- data/spec/arel/algebra/unit/relations/take_spec.rb +10 -0
- data/spec/arel/algebra/unit/relations/update_spec.rb +9 -0
- data/spec/arel/algebra/unit/relations/where_spec.rb +18 -0
- data/spec/arel/algebra/unit/session/session_spec.rb +84 -0
- data/spec/arel/engines/memory/integration/joins/cross_engine_spec.rb +48 -0
- data/spec/arel/engines/memory/unit/relations/array_spec.rb +32 -0
- data/spec/arel/engines/memory/unit/relations/insert_spec.rb +28 -0
- data/spec/arel/engines/memory/unit/relations/join_spec.rb +31 -0
- data/spec/arel/engines/memory/unit/relations/order_spec.rb +27 -0
- data/spec/arel/engines/memory/unit/relations/project_spec.rb +27 -0
- data/spec/arel/engines/memory/unit/relations/skip_spec.rb +26 -0
- data/spec/arel/engines/memory/unit/relations/take_spec.rb +26 -0
- data/spec/arel/engines/memory/unit/relations/where_spec.rb +39 -0
- data/spec/arel/engines/sql/integration/joins/with_adjacency_spec.rb +209 -0
- data/spec/arel/engines/sql/integration/joins/with_aggregations_spec.rb +167 -0
- data/spec/arel/engines/sql/integration/joins/with_compounds_spec.rb +107 -0
- data/spec/arel/engines/sql/unit/engine_spec.rb +45 -0
- data/spec/arel/engines/sql/unit/predicates/binary_spec.rb +117 -0
- data/spec/arel/engines/sql/unit/predicates/equality_spec.rb +46 -0
- data/spec/arel/engines/sql/unit/predicates/in_spec.rb +86 -0
- data/spec/arel/engines/sql/unit/predicates/predicates_spec.rb +65 -0
- data/spec/arel/engines/sql/unit/primitives/attribute_spec.rb +32 -0
- data/spec/arel/engines/sql/unit/primitives/expression_spec.rb +24 -0
- data/spec/arel/engines/sql/unit/primitives/literal_spec.rb +23 -0
- data/spec/arel/engines/sql/unit/primitives/value_spec.rb +29 -0
- data/spec/arel/engines/sql/unit/relations/alias_spec.rb +43 -0
- data/spec/arel/engines/sql/unit/relations/delete_spec.rb +63 -0
- data/spec/arel/engines/sql/unit/relations/group_spec.rb +56 -0
- data/spec/arel/engines/sql/unit/relations/insert_spec.rb +107 -0
- data/spec/arel/engines/sql/unit/relations/join_spec.rb +57 -0
- data/spec/arel/engines/sql/unit/relations/order_spec.rb +113 -0
- data/spec/arel/engines/sql/unit/relations/project_spec.rb +110 -0
- data/spec/arel/engines/sql/unit/relations/skip_spec.rb +32 -0
- data/spec/arel/engines/sql/unit/relations/table_spec.rb +69 -0
- data/spec/arel/engines/sql/unit/relations/take_spec.rb +32 -0
- data/spec/arel/engines/sql/unit/relations/update_spec.rb +151 -0
- data/spec/arel/engines/sql/unit/relations/where_spec.rb +56 -0
- data/spec/connections/mysql_connection.rb +16 -0
- data/spec/connections/postgresql_connection.rb +15 -0
- data/spec/connections/sqlite3_connection.rb +25 -0
- data/spec/doubles/hash.rb +23 -0
- data/spec/matchers/be_like.rb +24 -0
- data/spec/matchers/disambiguate_attributes.rb +28 -0
- data/spec/matchers/hash_the_same_as.rb +26 -0
- data/spec/schemas/mysql_schema.rb +18 -0
- data/spec/schemas/postgresql_schema.rb +18 -0
- data/spec/schemas/sqlite3_schema.rb +18 -0
- data/spec/spec_helper.rb +47 -0
- metadata +250 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
module Arel
|
2
|
+
class Ordering
|
3
|
+
delegate :relation, :to => :attribute
|
4
|
+
|
5
|
+
def bind(relation)
|
6
|
+
self.class.new(attribute.bind(relation))
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_ordering
|
10
|
+
self
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Ascending < Ordering
|
15
|
+
attributes :attribute
|
16
|
+
deriving :initialize, :==
|
17
|
+
end
|
18
|
+
|
19
|
+
class Descending < Ordering
|
20
|
+
attributes :attribute
|
21
|
+
deriving :initialize, :==
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'arel/algebra/relations/relation'
|
2
|
+
require 'arel/algebra/relations/utilities/compound'
|
3
|
+
require 'arel/algebra/relations/utilities/nil'
|
4
|
+
require 'arel/algebra/relations/utilities/externalization'
|
5
|
+
require 'arel/algebra/relations/row'
|
6
|
+
require 'arel/algebra/relations/writes'
|
7
|
+
require 'arel/algebra/relations/operations/alias'
|
8
|
+
require 'arel/algebra/relations/operations/group'
|
9
|
+
require 'arel/algebra/relations/operations/join'
|
10
|
+
require 'arel/algebra/relations/operations/order'
|
11
|
+
require 'arel/algebra/relations/operations/project'
|
12
|
+
require 'arel/algebra/relations/operations/where'
|
13
|
+
require 'arel/algebra/relations/operations/skip'
|
14
|
+
require 'arel/algebra/relations/operations/take'
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Arel
|
2
|
+
class Group < Compound
|
3
|
+
attributes :relation, :groupings
|
4
|
+
deriving :==
|
5
|
+
|
6
|
+
def initialize(relation, *groupings, &block)
|
7
|
+
@relation = relation
|
8
|
+
@groupings = (groupings + arguments_from_block(relation, &block)) \
|
9
|
+
.collect { |g| g.bind(relation) }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Arel
|
2
|
+
class Join < Relation
|
3
|
+
attributes :relation1, :relation2, :predicates
|
4
|
+
deriving :==
|
5
|
+
delegate :name, :to => :relation1
|
6
|
+
|
7
|
+
def initialize(relation1, relation2 = Nil.instance, *predicates)
|
8
|
+
@relation1, @relation2, @predicates = relation1, relation2, predicates
|
9
|
+
end
|
10
|
+
|
11
|
+
def hash
|
12
|
+
@hash ||= :relation1.hash
|
13
|
+
end
|
14
|
+
|
15
|
+
def eql?(other)
|
16
|
+
self == other
|
17
|
+
end
|
18
|
+
|
19
|
+
def attributes
|
20
|
+
@attributes ||= (relation1.externalize.attributes +
|
21
|
+
relation2.externalize.attributes).collect { |a| a.bind(self) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def wheres
|
25
|
+
# TESTME bind to self?
|
26
|
+
relation1.externalize.wheres
|
27
|
+
end
|
28
|
+
|
29
|
+
def ons
|
30
|
+
@ons ||= @predicates.collect { |p| p.bind(self) }
|
31
|
+
end
|
32
|
+
|
33
|
+
# TESTME
|
34
|
+
def externalizable?
|
35
|
+
relation1.externalizable? or relation2.externalizable?
|
36
|
+
end
|
37
|
+
|
38
|
+
def join?
|
39
|
+
true
|
40
|
+
end
|
41
|
+
|
42
|
+
def engine
|
43
|
+
relation1.engine != relation2.engine ? Memory::Engine.new : relation1.engine
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class InnerJoin < Join; end
|
48
|
+
class OuterJoin < Join; end
|
49
|
+
class StringJoin < Join
|
50
|
+
def attributes
|
51
|
+
relation1.externalize.attributes
|
52
|
+
end
|
53
|
+
|
54
|
+
def engine
|
55
|
+
relation1.engine
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class Relation
|
60
|
+
def join?
|
61
|
+
false
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Arel
|
2
|
+
class Order < Compound
|
3
|
+
attributes :relation, :orderings
|
4
|
+
deriving :==
|
5
|
+
|
6
|
+
def initialize(relation, *orderings, &block)
|
7
|
+
@relation = relation
|
8
|
+
@orderings = (orderings + arguments_from_block(relation, &block)) \
|
9
|
+
.collect { |o| o.bind(relation) }
|
10
|
+
end
|
11
|
+
|
12
|
+
# TESTME
|
13
|
+
def orders
|
14
|
+
# QUESTION - do we still need relation.orders ?
|
15
|
+
(orderings + relation.orders).collect { |o| o.bind(self) }.collect { |o| o.to_ordering }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Arel
|
2
|
+
class Project < Compound
|
3
|
+
attributes :relation, :projections
|
4
|
+
deriving :==
|
5
|
+
|
6
|
+
def initialize(relation, *projections, &block)
|
7
|
+
@relation = relation
|
8
|
+
@projections = (projections + arguments_from_block(relation, &block)) \
|
9
|
+
.collect { |p| p.bind(relation) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def attributes
|
13
|
+
@attributes ||= projections.collect { |p| p.bind(self) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def externalizable?
|
17
|
+
attributes.any?(&:aggregation?) or relation.externalizable?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Arel
|
2
|
+
class Where < Compound
|
3
|
+
attributes :relation, :predicate
|
4
|
+
deriving :==
|
5
|
+
|
6
|
+
def initialize(relation, *predicates, &block)
|
7
|
+
predicate = block_given?? yield(relation) : predicates.shift
|
8
|
+
@relation = predicates.empty?? relation : Where.new(relation, *predicates)
|
9
|
+
@predicate = predicate.bind(@relation)
|
10
|
+
end
|
11
|
+
|
12
|
+
def wheres
|
13
|
+
@wheres ||= (relation.wheres + [predicate]).collect { |p| p.bind(self) }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
module Arel
|
2
|
+
class Relation
|
3
|
+
attr_reader :count
|
4
|
+
|
5
|
+
def session
|
6
|
+
Session.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def call
|
10
|
+
engine.read(self)
|
11
|
+
end
|
12
|
+
|
13
|
+
def bind(relation)
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
module Enumerable
|
18
|
+
include ::Enumerable
|
19
|
+
|
20
|
+
def each(&block)
|
21
|
+
session.read(self).each(&block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def first
|
25
|
+
session.read(self).first
|
26
|
+
end
|
27
|
+
end
|
28
|
+
include Enumerable
|
29
|
+
|
30
|
+
module Operable
|
31
|
+
def join(other_relation = nil, join_class = InnerJoin)
|
32
|
+
case other_relation
|
33
|
+
when String
|
34
|
+
StringJoin.new(self, other_relation)
|
35
|
+
when Relation
|
36
|
+
JoinOperation.new(join_class, self, other_relation)
|
37
|
+
else
|
38
|
+
self
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def outer_join(other_relation = nil)
|
43
|
+
join(other_relation, OuterJoin)
|
44
|
+
end
|
45
|
+
|
46
|
+
[:where, :project, :order, :take, :skip, :group].each do |operation_name|
|
47
|
+
class_eval <<-OPERATION, __FILE__, __LINE__
|
48
|
+
def #{operation_name}(*arguments, &block)
|
49
|
+
arguments.all?(&:blank?) && !block_given?? self : #{operation_name.to_s.classify}.new(self, *arguments, &block)
|
50
|
+
end
|
51
|
+
OPERATION
|
52
|
+
end
|
53
|
+
|
54
|
+
def alias
|
55
|
+
Alias.new(self)
|
56
|
+
end
|
57
|
+
|
58
|
+
module Writable
|
59
|
+
def insert(record)
|
60
|
+
session.create Insert.new(self, record)
|
61
|
+
end
|
62
|
+
|
63
|
+
def update(assignments)
|
64
|
+
session.update Update.new(self, assignments)
|
65
|
+
end
|
66
|
+
|
67
|
+
def delete
|
68
|
+
session.delete Deletion.new(self)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
include Writable
|
72
|
+
|
73
|
+
JoinOperation = Struct.new(:join_class, :relation1, :relation2) do
|
74
|
+
def on(*predicates)
|
75
|
+
join_class.new(relation1, relation2, *predicates)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
include Operable
|
80
|
+
|
81
|
+
module AttributeAccessable
|
82
|
+
def [](index)
|
83
|
+
case index
|
84
|
+
when Symbol, String
|
85
|
+
find_attribute_matching_name(index)
|
86
|
+
when Attribute, Expression
|
87
|
+
find_attribute_matching_attribute(index)
|
88
|
+
when ::Array
|
89
|
+
# TESTME
|
90
|
+
index.collect { |i| self[i] }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def find_attribute_matching_name(name)
|
95
|
+
attributes.detect { |a| a.named?(name) }
|
96
|
+
end
|
97
|
+
|
98
|
+
def find_attribute_matching_attribute(attribute)
|
99
|
+
matching_attributes(attribute).max do |a1, a2|
|
100
|
+
(a1.original_attribute / attribute) <=> (a2.original_attribute / attribute)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def position_of(attribute)
|
105
|
+
(@position_of ||= Hash.new do |h, attribute|
|
106
|
+
h[attribute] = attributes.index(self[attribute])
|
107
|
+
end)[attribute]
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
def matching_attributes(attribute)
|
112
|
+
(@matching_attributes ||= attributes.inject({}) do |hash, a|
|
113
|
+
(hash[a.root] ||= []) << a
|
114
|
+
hash
|
115
|
+
end)[attribute.root] || []
|
116
|
+
end
|
117
|
+
|
118
|
+
def has_attribute?(attribute)
|
119
|
+
!matching_attributes(attribute).empty?
|
120
|
+
end
|
121
|
+
end
|
122
|
+
include AttributeAccessable
|
123
|
+
|
124
|
+
module DefaultOperations
|
125
|
+
def attributes; [] end
|
126
|
+
def wheres; [] end
|
127
|
+
def orders; [] end
|
128
|
+
def inserts; [] end
|
129
|
+
def groupings; [] end
|
130
|
+
def joins(formatter = nil); nil end # FIXME
|
131
|
+
def taken; nil end
|
132
|
+
def skipped; nil end
|
133
|
+
end
|
134
|
+
include DefaultOperations
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Arel
|
2
|
+
class Row
|
3
|
+
attributes :relation, :tuple
|
4
|
+
deriving :==, :initialize
|
5
|
+
|
6
|
+
def [](attribute)
|
7
|
+
attribute.type_cast(tuple[relation.position_of(attribute)])
|
8
|
+
end
|
9
|
+
|
10
|
+
def slice(*attributes)
|
11
|
+
Row.new(relation, attributes.inject([]) do |cheese, attribute|
|
12
|
+
# FIXME TESTME method chaining
|
13
|
+
cheese << tuple[relation.relation.position_of(attribute)]
|
14
|
+
cheese
|
15
|
+
end)
|
16
|
+
end
|
17
|
+
|
18
|
+
def bind(relation)
|
19
|
+
Row.new(relation, tuple)
|
20
|
+
end
|
21
|
+
|
22
|
+
def combine(other, relation)
|
23
|
+
Row.new(relation, tuple + other.tuple)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Arel
|
2
|
+
class Compound < Relation
|
3
|
+
attr_reader :relation
|
4
|
+
delegate :joins, :join?, :inserts, :taken, :skipped, :name, :externalizable?,
|
5
|
+
:column_for, :engine,
|
6
|
+
:to => :relation
|
7
|
+
|
8
|
+
[:attributes, :wheres, :groupings, :orders].each do |operation_name|
|
9
|
+
class_eval <<-OPERATION, __FILE__, __LINE__
|
10
|
+
def #{operation_name}
|
11
|
+
@#{operation_name} ||= relation.#{operation_name}.collect { |o| o.bind(self) }
|
12
|
+
end
|
13
|
+
OPERATION
|
14
|
+
end
|
15
|
+
|
16
|
+
def hash
|
17
|
+
@hash ||= :relation.hash
|
18
|
+
end
|
19
|
+
|
20
|
+
def eql?(other)
|
21
|
+
self == other
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def arguments_from_block(relation, &block)
|
27
|
+
block_given?? [yield(relation)] : []
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Arel
|
2
|
+
class Externalization < Compound
|
3
|
+
attributes :relation
|
4
|
+
deriving :initialize, :==
|
5
|
+
|
6
|
+
def wheres
|
7
|
+
[]
|
8
|
+
end
|
9
|
+
|
10
|
+
def attributes
|
11
|
+
@attributes ||= relation.attributes.collect { |a| a.to_attribute(self) }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Relation
|
16
|
+
def externalize
|
17
|
+
@externalized ||= externalizable?? Externalization.new(self) : self
|
18
|
+
end
|
19
|
+
|
20
|
+
def externalizable?
|
21
|
+
false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|