ffi-geos 1.2.1 → 1.2.2
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 +5 -5
- data/.rubocop.yml +20 -0
- data/.travis.yml +7 -3
- data/Gemfile +1 -1
- data/Guardfile +4 -5
- data/ffi-geos.gemspec +1 -1
- data/lib/ffi-geos.rb +212 -196
- data/lib/ffi-geos/buffer_params.rb +9 -20
- data/lib/ffi-geos/coordinate_sequence.rb +342 -58
- data/lib/ffi-geos/geometry.rb +167 -178
- data/lib/ffi-geos/geometry_collection.rb +60 -12
- data/lib/ffi-geos/interrupt.rb +2 -4
- data/lib/ffi-geos/line_string.rb +146 -37
- data/lib/ffi-geos/linear_ring.rb +2 -3
- data/lib/ffi-geos/multi_line_string.rb +1 -2
- data/lib/ffi-geos/multi_point.rb +0 -1
- data/lib/ffi-geos/multi_polygon.rb +0 -1
- data/lib/ffi-geos/point.rb +69 -14
- data/lib/ffi-geos/polygon.rb +110 -21
- data/lib/ffi-geos/prepared_geometry.rb +11 -12
- data/lib/ffi-geos/strtree.rb +41 -52
- data/lib/ffi-geos/tools.rb +15 -18
- data/lib/ffi-geos/utils.rb +27 -44
- data/lib/ffi-geos/version.rb +1 -3
- data/lib/ffi-geos/wkb_reader.rb +4 -9
- data/lib/ffi-geos/wkb_writer.rb +14 -18
- data/lib/ffi-geos/wkt_reader.rb +2 -5
- data/lib/ffi-geos/wkt_writer.rb +17 -22
- data/test/.rubocop.yml +36 -0
- data/test/coordinate_sequence_tests.rb +263 -14
- data/test/geometry_collection_tests.rb +412 -1
- data/test/geometry_tests.rb +156 -86
- data/test/interrupt_tests.rb +2 -4
- data/test/line_string_tests.rb +212 -23
- data/test/linear_ring_tests.rb +1 -2
- data/test/misc_tests.rb +28 -29
- data/test/multi_line_string_tests.rb +0 -1
- data/test/point_tests.rb +158 -1
- data/test/polygon_tests.rb +284 -1
- data/test/prepared_geometry_tests.rb +1 -3
- data/test/strtree_tests.rb +9 -10
- data/test/test_helper.rb +49 -18
- data/test/tools_tests.rb +1 -3
- data/test/utils_tests.rb +22 -22
- data/test/wkb_reader_tests.rb +10 -9
- data/test/wkb_writer_tests.rb +5 -13
- data/test/wkt_reader_tests.rb +1 -2
- data/test/wkt_writer_tests.rb +9 -14
- metadata +6 -3
data/lib/ffi-geos/polygon.rb
CHANGED
@@ -1,41 +1,130 @@
|
|
1
|
-
# encoding: UTF-8
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
3
|
module Geos
|
5
4
|
class Polygon < Geometry
|
6
5
|
def num_interior_rings
|
7
|
-
FFIGeos.GEOSGetNumInteriorRings_r(Geos.current_handle_pointer,
|
6
|
+
FFIGeos.GEOSGetNumInteriorRings_r(Geos.current_handle_pointer, ptr)
|
8
7
|
end
|
9
8
|
|
10
9
|
def interior_ring_n(n)
|
11
|
-
if n
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
}
|
20
|
-
)
|
21
|
-
end
|
10
|
+
raise Geos::IndexBoundsError if n.negative? || n >= num_interior_rings
|
11
|
+
|
12
|
+
cast_geometry_ptr(
|
13
|
+
FFIGeos.GEOSGetInteriorRingN_r(Geos.current_handle_pointer, ptr, n),
|
14
|
+
auto_free: false,
|
15
|
+
srid_copy: srid,
|
16
|
+
parent: self
|
17
|
+
)
|
22
18
|
end
|
23
|
-
|
19
|
+
alias interior_ring interior_ring_n
|
24
20
|
|
25
21
|
def exterior_ring
|
26
22
|
cast_geometry_ptr(
|
27
|
-
FFIGeos.GEOSGetExteriorRing_r(Geos.current_handle_pointer,
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
}
|
23
|
+
FFIGeos.GEOSGetExteriorRing_r(Geos.current_handle_pointer, ptr),
|
24
|
+
auto_free: false,
|
25
|
+
srid_copy: srid,
|
26
|
+
parent: self
|
32
27
|
)
|
33
28
|
end
|
34
29
|
|
35
30
|
def interior_rings
|
36
|
-
|
37
|
-
|
31
|
+
num_interior_rings.times.collect do |n|
|
32
|
+
interior_ring_n(n)
|
38
33
|
end
|
39
34
|
end
|
35
|
+
|
36
|
+
def dump_points(cur_path = [])
|
37
|
+
points = [ exterior_ring.dump_points ]
|
38
|
+
|
39
|
+
interior_rings.each do |ring|
|
40
|
+
points.push(ring.dump_points)
|
41
|
+
end
|
42
|
+
|
43
|
+
cur_path.concat(points)
|
44
|
+
end
|
45
|
+
|
46
|
+
def snap_to_grid!(*args)
|
47
|
+
if !self.empty?
|
48
|
+
exterior_ring = self.exterior_ring.coord_seq.snap_to_grid!(*args)
|
49
|
+
|
50
|
+
if exterior_ring.length == 0
|
51
|
+
@ptr = Geos.create_empty_polygon(:srid => self.srid).ptr
|
52
|
+
elsif exterior_ring.length < 4
|
53
|
+
raise Geos::InvalidGeometryError.new("snap_to_grid! produced an invalid number of points in exterior ring - found #{exterior_ring.length} - must be 0 or >= 4")
|
54
|
+
else
|
55
|
+
interior_rings = []
|
56
|
+
|
57
|
+
self.num_interior_rings.times { |i|
|
58
|
+
interior_ring = self.interior_ring_n(i).coord_seq.snap_to_grid!(*args)
|
59
|
+
|
60
|
+
interior_rings << interior_ring unless interior_ring.length < 4
|
61
|
+
}
|
62
|
+
|
63
|
+
interior_rings.compact!
|
64
|
+
|
65
|
+
polygon = Geos.create_polygon(exterior_ring, interior_rings, :srid => self.srid)
|
66
|
+
@ptr = polygon.ptr
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
def snap_to_grid(*args)
|
74
|
+
ret = self.dup.snap_to_grid!(*args)
|
75
|
+
ret.srid = pick_srid_according_to_policy(self.srid)
|
76
|
+
ret
|
77
|
+
end
|
78
|
+
|
79
|
+
%w{ max min }.each do |op|
|
80
|
+
%w{ x y }.each do |dimension|
|
81
|
+
self.class_eval(<<-EOF, __FILE__, __LINE__ + 1)
|
82
|
+
def #{dimension}_#{op}
|
83
|
+
unless self.empty?
|
84
|
+
self.envelope.exterior_ring.#{dimension}_#{op}
|
85
|
+
end
|
86
|
+
end
|
87
|
+
EOF
|
88
|
+
end
|
89
|
+
|
90
|
+
self.class_eval(<<-EOF, __FILE__, __LINE__ + 1)
|
91
|
+
def z_#{op}
|
92
|
+
unless self.empty?
|
93
|
+
if self.has_z?
|
94
|
+
self.exterior_ring.z_#{op}
|
95
|
+
else
|
96
|
+
0
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
EOF
|
101
|
+
end
|
102
|
+
|
103
|
+
%w{
|
104
|
+
affine
|
105
|
+
rotate
|
106
|
+
rotate_x
|
107
|
+
rotate_y
|
108
|
+
rotate_z
|
109
|
+
scale
|
110
|
+
trans_scale
|
111
|
+
translate
|
112
|
+
}.each do |m|
|
113
|
+
self.class_eval(<<-EOF, __FILE__, __LINE__ + 1)
|
114
|
+
def #{m}!(*args)
|
115
|
+
self.exterior_ring.coord_seq.#{m}!(*args)
|
116
|
+
self.interior_rings.each do |ring|
|
117
|
+
ring.coord_seq.#{m}!(*args)
|
118
|
+
end
|
119
|
+
self
|
120
|
+
end
|
121
|
+
|
122
|
+
def #{m}(*args)
|
123
|
+
ret = self.dup.#{m}!(*args)
|
124
|
+
ret.srid = pick_srid_according_to_policy(self.srid)
|
125
|
+
ret
|
126
|
+
end
|
127
|
+
EOF
|
128
|
+
end
|
40
129
|
end
|
41
130
|
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
# encoding: UTF-8
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
3
|
module Geos
|
@@ -13,7 +12,7 @@ module Geos
|
|
13
12
|
check_geometry(geom)
|
14
13
|
|
15
14
|
options = {
|
16
|
-
:
|
15
|
+
auto_free: true
|
17
16
|
}.merge(options)
|
18
17
|
|
19
18
|
@ptr = FFI::AutoPointer.new(
|
@@ -31,52 +30,52 @@ module Geos
|
|
31
30
|
|
32
31
|
def contains?(geom)
|
33
32
|
check_geometry(geom)
|
34
|
-
bool_result(FFIGeos.GEOSPreparedContains_r(Geos.current_handle_pointer,
|
33
|
+
bool_result(FFIGeos.GEOSPreparedContains_r(Geos.current_handle_pointer, ptr, geom.ptr))
|
35
34
|
end
|
36
35
|
|
37
36
|
def contains_properly?(geom)
|
38
37
|
check_geometry(geom)
|
39
|
-
bool_result(FFIGeos.GEOSPreparedContainsProperly_r(Geos.current_handle_pointer,
|
38
|
+
bool_result(FFIGeos.GEOSPreparedContainsProperly_r(Geos.current_handle_pointer, ptr, geom.ptr))
|
40
39
|
end
|
41
40
|
|
42
41
|
def covered_by?(geom)
|
43
42
|
check_geometry(geom)
|
44
|
-
bool_result(FFIGeos.GEOSPreparedCoveredBy_r(Geos.current_handle_pointer,
|
43
|
+
bool_result(FFIGeos.GEOSPreparedCoveredBy_r(Geos.current_handle_pointer, ptr, geom.ptr))
|
45
44
|
end
|
46
45
|
|
47
46
|
def covers?(geom)
|
48
47
|
check_geometry(geom)
|
49
|
-
bool_result(FFIGeos.GEOSPreparedCovers_r(Geos.current_handle_pointer,
|
48
|
+
bool_result(FFIGeos.GEOSPreparedCovers_r(Geos.current_handle_pointer, ptr, geom.ptr))
|
50
49
|
end
|
51
50
|
|
52
51
|
def crosses?(geom)
|
53
52
|
check_geometry(geom)
|
54
|
-
bool_result(FFIGeos.GEOSPreparedCrosses_r(Geos.current_handle_pointer,
|
53
|
+
bool_result(FFIGeos.GEOSPreparedCrosses_r(Geos.current_handle_pointer, ptr, geom.ptr))
|
55
54
|
end
|
56
55
|
|
57
56
|
def disjoint?(geom)
|
58
57
|
check_geometry(geom)
|
59
|
-
bool_result(FFIGeos.GEOSPreparedDisjoint_r(Geos.current_handle_pointer,
|
58
|
+
bool_result(FFIGeos.GEOSPreparedDisjoint_r(Geos.current_handle_pointer, ptr, geom.ptr))
|
60
59
|
end
|
61
60
|
|
62
61
|
def intersects?(geom)
|
63
62
|
check_geometry(geom)
|
64
|
-
bool_result(FFIGeos.GEOSPreparedIntersects_r(Geos.current_handle_pointer,
|
63
|
+
bool_result(FFIGeos.GEOSPreparedIntersects_r(Geos.current_handle_pointer, ptr, geom.ptr))
|
65
64
|
end
|
66
65
|
|
67
66
|
def overlaps?(geom)
|
68
67
|
check_geometry(geom)
|
69
|
-
bool_result(FFIGeos.GEOSPreparedOverlaps_r(Geos.current_handle_pointer,
|
68
|
+
bool_result(FFIGeos.GEOSPreparedOverlaps_r(Geos.current_handle_pointer, ptr, geom.ptr))
|
70
69
|
end
|
71
70
|
|
72
71
|
def touches?(geom)
|
73
72
|
check_geometry(geom)
|
74
|
-
bool_result(FFIGeos.GEOSPreparedTouches_r(Geos.current_handle_pointer,
|
73
|
+
bool_result(FFIGeos.GEOSPreparedTouches_r(Geos.current_handle_pointer, ptr, geom.ptr))
|
75
74
|
end
|
76
75
|
|
77
76
|
def within?(geom)
|
78
77
|
check_geometry(geom)
|
79
|
-
bool_result(FFIGeos.GEOSPreparedWithin_r(Geos.current_handle_pointer,
|
78
|
+
bool_result(FFIGeos.GEOSPreparedWithin_r(Geos.current_handle_pointer, ptr, geom.ptr))
|
80
79
|
end
|
81
80
|
end
|
82
81
|
end
|
data/lib/ffi-geos/strtree.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# encoding: UTF-8
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
3
|
module Geos
|
@@ -12,7 +11,7 @@ module Geos
|
|
12
11
|
|
13
12
|
class AlreadyBuiltError < Geos::Error
|
14
13
|
def initialize(*)
|
15
|
-
super(
|
14
|
+
super('STRtree has already been built')
|
16
15
|
end
|
17
16
|
end
|
18
17
|
|
@@ -33,13 +32,13 @@ module Geos
|
|
33
32
|
args
|
34
33
|
end
|
35
34
|
|
36
|
-
geoms_and_objects.each do |geom,
|
35
|
+
geoms_and_objects.each do |geom, _obj|
|
37
36
|
check_geometry(geom)
|
38
37
|
end
|
39
38
|
end
|
40
39
|
|
41
40
|
if capacity <= 0
|
42
|
-
raise ArgumentError
|
41
|
+
raise ArgumentError, 'STRtree capacity must be greater than 0'
|
43
42
|
end
|
44
43
|
|
45
44
|
ptr = FFIGeos.GEOSSTRtree_create_r(Geos.current_handle_pointer, capacity)
|
@@ -55,10 +54,10 @@ module Geos
|
|
55
54
|
@storage_key = 0
|
56
55
|
@built = false
|
57
56
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
57
|
+
return unless geoms_and_objects
|
58
|
+
|
59
|
+
geoms_and_objects.each do |geom, obj|
|
60
|
+
insert(geom, obj)
|
62
61
|
end
|
63
62
|
end
|
64
63
|
|
@@ -80,23 +79,21 @@ module Geos
|
|
80
79
|
private :next_key
|
81
80
|
|
82
81
|
def insert(geom, item = nil)
|
83
|
-
if
|
84
|
-
raise AlreadyBuiltError.new
|
85
|
-
else
|
86
|
-
check_geometry(geom)
|
82
|
+
raise AlreadyBuiltError if built?
|
87
83
|
|
88
|
-
|
89
|
-
key_ptr = FFI::MemoryPointer.new(:pointer)
|
90
|
-
key_ptr.write_int(key)
|
84
|
+
check_geometry(geom)
|
91
85
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
}
|
96
|
-
@ptrs[key] = key_ptr
|
86
|
+
key = next_key
|
87
|
+
key_ptr = FFI::MemoryPointer.new(:pointer)
|
88
|
+
key_ptr.write_int(key)
|
97
89
|
|
98
|
-
|
99
|
-
|
90
|
+
@storage[key] = {
|
91
|
+
item: item,
|
92
|
+
geometry: geom
|
93
|
+
}
|
94
|
+
@ptrs[key] = key_ptr
|
95
|
+
|
96
|
+
FFIGeos.GEOSSTRtree_insert_r(Geos.current_handle_pointer, ptr, geom.ptr, key_ptr)
|
100
97
|
end
|
101
98
|
|
102
99
|
def remove(geom, item)
|
@@ -106,15 +103,13 @@ module Geos
|
|
106
103
|
storage[0]
|
107
104
|
end
|
108
105
|
|
109
|
-
|
110
|
-
key_ptr = @ptrs[key]
|
111
|
-
result = FFIGeos.GEOSSTRtree_remove_r(Geos.current_handle_pointer, self.ptr, geom.ptr, key_ptr)
|
112
|
-
built!
|
106
|
+
return unless key
|
113
107
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
108
|
+
key_ptr = @ptrs[key]
|
109
|
+
result = FFIGeos.GEOSSTRtree_remove_r(Geos.current_handle_pointer, ptr, geom.ptr, key_ptr)
|
110
|
+
built!
|
111
|
+
|
112
|
+
@storage.delete(key) if result == 1
|
118
113
|
end
|
119
114
|
|
120
115
|
def query_all(geom)
|
@@ -128,14 +123,12 @@ module Geos
|
|
128
123
|
storage = @storage[key]
|
129
124
|
retval << storage
|
130
125
|
|
131
|
-
if block_given?
|
132
|
-
yield(storage)
|
133
|
-
end
|
126
|
+
yield(storage) if block_given?
|
134
127
|
}
|
135
128
|
|
136
129
|
FFIGeos.GEOSSTRtree_query_r(
|
137
130
|
Geos.current_handle_pointer,
|
138
|
-
|
131
|
+
ptr,
|
139
132
|
geom.ptr,
|
140
133
|
callback,
|
141
134
|
nil
|
@@ -145,12 +138,12 @@ module Geos
|
|
145
138
|
end
|
146
139
|
|
147
140
|
def query(geom, ret = :item)
|
148
|
-
|
141
|
+
query_all(geom).collect { |storage|
|
149
142
|
item = if ret.is_a?(Array)
|
150
143
|
storage.inject({}) do |memo, k|
|
151
|
-
memo.tap
|
144
|
+
memo.tap do
|
152
145
|
memo[k] = storage[k]
|
153
|
-
|
146
|
+
end
|
154
147
|
end
|
155
148
|
elsif ret == :all
|
156
149
|
storage
|
@@ -158,27 +151,23 @@ module Geos
|
|
158
151
|
storage[ret]
|
159
152
|
end
|
160
153
|
|
161
|
-
item.tap
|
162
|
-
if block_given?
|
163
|
-
|
164
|
-
end
|
165
|
-
}
|
154
|
+
item.tap do
|
155
|
+
yield(item) if block_given?
|
156
|
+
end
|
166
157
|
}.compact
|
167
158
|
end
|
168
159
|
|
169
160
|
def query_geometries(geom)
|
170
|
-
|
171
|
-
storage[:geometry].tap
|
172
|
-
if block_given?
|
173
|
-
|
174
|
-
end
|
175
|
-
}
|
161
|
+
query_all(geom).collect { |storage|
|
162
|
+
storage[:geometry].tap do |val|
|
163
|
+
yield(val) if block_given?
|
164
|
+
end
|
176
165
|
}.compact
|
177
166
|
end
|
178
|
-
|
167
|
+
alias query_geoms query_geometries
|
179
168
|
|
180
169
|
def iterate
|
181
|
-
@storage.
|
170
|
+
@storage.each_value do |v|
|
182
171
|
yield(v)
|
183
172
|
end
|
184
173
|
end
|
@@ -205,7 +194,7 @@ module Geos
|
|
205
194
|
|
206
195
|
key_ptr = FFIGeos.GEOSSTRtree_nearest_generic_r(
|
207
196
|
Geos.current_handle_pointer,
|
208
|
-
|
197
|
+
ptr,
|
209
198
|
geom.ptr,
|
210
199
|
geom.envelope.ptr,
|
211
200
|
callback,
|
@@ -219,7 +208,7 @@ module Geos
|
|
219
208
|
item = nearest_generic(geom)
|
220
209
|
item[:geometry] if item
|
221
210
|
end
|
222
|
-
|
211
|
+
alias nearest_geometry nearest
|
223
212
|
|
224
213
|
def nearest_item(geom)
|
225
214
|
item = nearest_generic(geom)
|
data/lib/ffi-geos/tools.rb
CHANGED
@@ -1,16 +1,15 @@
|
|
1
|
-
# encoding: UTF-8
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
3
|
module Geos
|
5
4
|
class NullPointerError < Geos::Error
|
6
5
|
def initialize(*)
|
7
|
-
super(
|
6
|
+
super('Tried to create a Geometry from a NULL pointer!')
|
8
7
|
end
|
9
8
|
end
|
10
9
|
|
11
10
|
class InvalidGeometryTypeError < Geos::Error
|
12
11
|
def initialize(*)
|
13
|
-
super(
|
12
|
+
super('Invalid geometry type')
|
14
13
|
end
|
15
14
|
end
|
16
15
|
|
@@ -25,12 +24,10 @@ module Geos
|
|
25
24
|
|
26
25
|
def cast_geometry_ptr(geom_ptr, options = {})
|
27
26
|
options = {
|
28
|
-
:
|
27
|
+
auto_free: true
|
29
28
|
}.merge(options)
|
30
29
|
|
31
|
-
if geom_ptr.null?
|
32
|
-
raise Geos::NullPointerError.new
|
33
|
-
end
|
30
|
+
raise Geos::NullPointerError if geom_ptr.null?
|
34
31
|
|
35
32
|
klass = case FFIGeos.GEOSGeomTypeId_r(Geos.current_handle_pointer, geom_ptr)
|
36
33
|
when GEOS_POINT
|
@@ -53,7 +50,7 @@ module Geos
|
|
53
50
|
raise Geos::InvalidGeometryTypeError.new
|
54
51
|
end
|
55
52
|
|
56
|
-
klass.new(geom_ptr, options).tap
|
53
|
+
klass.new(geom_ptr, options).tap do |ret|
|
57
54
|
if options[:srid]
|
58
55
|
ret.srid = options[:srid] || 0
|
59
56
|
elsif options[:srid_copy]
|
@@ -63,11 +60,11 @@ module Geos
|
|
63
60
|
options[:srid_copy] || 0
|
64
61
|
end
|
65
62
|
end
|
66
|
-
|
63
|
+
end
|
67
64
|
end
|
68
65
|
|
69
66
|
def check_geometry(geom)
|
70
|
-
raise TypeError
|
67
|
+
raise TypeError, 'Expected Geos::Geometry' unless geom.is_a?(Geos::Geometry)
|
71
68
|
end
|
72
69
|
|
73
70
|
def pick_srid_from_geoms(srid_a, srid_b, policy = Geos.srid_copy_policy)
|
@@ -81,7 +78,7 @@ module Geos
|
|
81
78
|
when :strict
|
82
79
|
raise Geos::MixedSRIDsError.new(srid_a, srid_b)
|
83
80
|
else
|
84
|
-
raise ArgumentError
|
81
|
+
raise ArgumentError, "Unexpected policy value: #{policy}"
|
85
82
|
end
|
86
83
|
end
|
87
84
|
|
@@ -97,12 +94,12 @@ module Geos
|
|
97
94
|
|
98
95
|
def bool_result(result)
|
99
96
|
case result
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
97
|
+
when 1
|
98
|
+
true
|
99
|
+
when 0
|
100
|
+
false
|
101
|
+
else
|
102
|
+
raise Geos::UnexpectedBooleanResultError, result
|
106
103
|
end
|
107
104
|
end
|
108
105
|
|
@@ -116,7 +113,7 @@ module Geos
|
|
116
113
|
|
117
114
|
def check_enum_value(enum, value)
|
118
115
|
enum[value] or
|
119
|
-
raise TypeError
|
116
|
+
raise TypeError, "Couldn't find valid #{enum.tag} value: #{value}"
|
120
117
|
end
|
121
118
|
|
122
119
|
def symbol_for_enum(enum, value)
|