clevic 0.13.0.b9 → 0.13.0.b10
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +3 -0
- data/lib/clevic/action_builder.rb +16 -16
- data/lib/clevic/ar_methods.rb +22 -22
- data/lib/clevic/attribute_list.rb +5 -5
- data/lib/clevic/cache_table.rb +18 -18
- data/lib/clevic/dataset_roller.rb +3 -3
- data/lib/clevic/default_view.rb +4 -4
- data/lib/clevic/delegate.rb +8 -8
- data/lib/clevic/delegates/combo_delegate.rb +22 -22
- data/lib/clevic/delegates/distinct_delegate.rb +5 -5
- data/lib/clevic/delegates/relational_delegate.rb +6 -6
- data/lib/clevic/delegates/set_delegate.rb +3 -3
- data/lib/clevic/dirty.rb +1 -1
- data/lib/clevic/emitter.rb +3 -3
- data/lib/clevic/extensions.rb +4 -4
- data/lib/clevic/field.rb +68 -68
- data/lib/clevic/field_valuer.rb +26 -26
- data/lib/clevic/filter_command.rb +6 -6
- data/lib/clevic/framework.rb +3 -3
- data/lib/clevic/generic_format.rb +2 -2
- data/lib/clevic/many_field.rb +3 -3
- data/lib/clevic/model_builder.rb +88 -84
- data/lib/clevic/model_column.rb +13 -13
- data/lib/clevic/ordered_dataset.rb +7 -7
- data/lib/clevic/qt/action_builder.rb +3 -3
- data/lib/clevic/qt/browser.rb +28 -28
- data/lib/clevic/qt/clipboard.rb +5 -5
- data/lib/clevic/qt/combo_delegate.rb +12 -12
- data/lib/clevic/qt/distinct_delegate.rb +1 -1
- data/lib/clevic/qt/extensions.rb +4 -4
- data/lib/clevic/qt/qt_combo_box.rb +7 -7
- data/lib/clevic/qt/relational_delegate.rb +5 -5
- data/lib/clevic/qt/search_dialog.rb +15 -15
- data/lib/clevic/qt/simplest_delegate.rb +2 -2
- data/lib/clevic/qt/table_model.rb +46 -46
- data/lib/clevic/qt/table_view.rb +48 -48
- data/lib/clevic/qt/text_delegate.rb +9 -9
- data/lib/clevic/rails_models_loaders.rb +3 -3
- data/lib/clevic/record.rb +8 -8
- data/lib/clevic/sampler.rb +6 -6
- data/lib/clevic/sequel_ar_adapter.rb +22 -22
- data/lib/clevic/sequel_clevic.rb +10 -10
- data/lib/clevic/sequel_meta.rb +5 -5
- data/lib/clevic/sequel_naked.rb +4 -4
- data/lib/clevic/swing/action.rb +20 -20
- data/lib/clevic/swing/action_builder.rb +2 -2
- data/lib/clevic/swing/boolean_delegate.rb +3 -3
- data/lib/clevic/swing/browser.rb +37 -37
- data/lib/clevic/swing/cell_editor.rb +13 -13
- data/lib/clevic/swing/cell_renderer.rb +7 -7
- data/lib/clevic/swing/clipboard.rb +19 -19
- data/lib/clevic/swing/combo_delegate.rb +26 -26
- data/lib/clevic/swing/confirm_dialog.rb +7 -7
- data/lib/clevic/swing/delegate.rb +4 -4
- data/lib/clevic/swing/extensions.rb +24 -24
- data/lib/clevic/swing/field.rb +1 -1
- data/lib/clevic/swing/relational_delegate.rb +2 -2
- data/lib/clevic/swing/row_header.rb +32 -32
- data/lib/clevic/swing/search_dialog.rb +31 -31
- data/lib/clevic/swing/selection_model.rb +12 -12
- data/lib/clevic/swing/swing_table_index.rb +6 -6
- data/lib/clevic/swing/table_model.rb +30 -30
- data/lib/clevic/swing/table_view.rb +54 -54
- data/lib/clevic/swing/table_view_focus.rb +4 -4
- data/lib/clevic/swing/tag_delegate.rb +14 -14
- data/lib/clevic/swing/tag_editor.rb +4 -4
- data/lib/clevic/swing/text_area_delegate.rb +6 -6
- data/lib/clevic/swing/text_delegate.rb +4 -4
- data/lib/clevic/table_index.rb +14 -14
- data/lib/clevic/table_model.rb +30 -30
- data/lib/clevic/table_searcher.rb +19 -19
- data/lib/clevic/table_view.rb +92 -92
- data/lib/clevic/table_view_paste.rb +19 -19
- data/lib/clevic/version.rb +1 -1
- data/lib/clevic/view.rb +22 -22
- data/models/accounts_models.rb +10 -10
- data/models/examples.rb +2 -2
- data/models/times_models.rb +32 -32
- data/models/values_models.rb +2 -2
- data/test/test_cache_table.rb +15 -15
- data/test/test_helper.rb +7 -7
- data/test/test_model_index_extensions.rb +6 -6
- data/test/test_table_model.rb +6 -6
- data/test/test_table_searcher.rb +25 -25
- metadata +33 -35
@@ -8,14 +8,14 @@ class TagEditor < javax.swing.JComponent
|
|
8
8
|
def initialize( field )
|
9
9
|
super()
|
10
10
|
@field = field
|
11
|
-
|
11
|
+
|
12
12
|
create_fields
|
13
13
|
init_layout
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
attr_reader :field
|
17
17
|
attr_reader :entity
|
18
|
-
|
18
|
+
|
19
19
|
# Hopefully called by the editor framework
|
20
20
|
# might be init_component
|
21
21
|
def configureEditor( editor, entity )
|
@@ -34,7 +34,7 @@ class TagEditor < javax.swing.JComponent
|
|
34
34
|
@add_button = javax.swing.JButton.new
|
35
35
|
@remove_button = javax.swing.JButton.new
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
def init_layout
|
39
39
|
# This is mostly cut-n-pasted from the Netbeans Java sources. So don't tweak it.
|
40
40
|
text_field.setName("text_field");
|
@@ -12,7 +12,7 @@ module Clevic
|
|
12
12
|
super( newrect )
|
13
13
|
end
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
class TextAreaDelegate < Delegate
|
17
17
|
# TODO check that Ctrl-VK_ENTER stops editing
|
18
18
|
def init_component( cell_editor )
|
@@ -21,23 +21,23 @@ module Clevic
|
|
21
21
|
text_component.rows = ( edit_value.andand.count( "\n" ) || 0 ) + 2
|
22
22
|
text_component.select_all
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
def editor
|
26
26
|
@editor ||= EditorScrollPane.new( text_component, javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS )
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
def text_component
|
30
30
|
@text_component ||= javax.swing.JTextArea.new
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
def value
|
34
34
|
text_component.text
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
37
|
def minimal_edit
|
38
38
|
text_component.select_all
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
def needs_pre_selection?
|
42
42
|
true
|
43
43
|
end
|
@@ -8,21 +8,21 @@ module Clevic
|
|
8
8
|
editor.text = edit_value
|
9
9
|
editor.select_all
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
def editor
|
13
13
|
@editor ||= javax.swing.JTextField.new.tap do |e|
|
14
14
|
e.horizontal_alignment = field.swing_alignment
|
15
15
|
end
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
def value
|
19
19
|
editor.text
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
def minimal_edit
|
23
23
|
editor.select_all
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
def needs_pre_selection?
|
27
27
|
true
|
28
28
|
end
|
data/lib/clevic/table_index.rb
CHANGED
@@ -4,12 +4,12 @@ module Clevic
|
|
4
4
|
# to be included in something that responds to model, row, column
|
5
5
|
module TableIndex
|
6
6
|
include FieldValuer
|
7
|
-
|
7
|
+
|
8
8
|
# return the Clevic::Field for this index
|
9
9
|
def field
|
10
10
|
@field ||= model.field_for_index( self )
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
def dump
|
14
14
|
if valid?
|
15
15
|
<<-EOF
|
@@ -21,35 +21,35 @@ module Clevic
|
|
21
21
|
'invalid'
|
22
22
|
end
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
def prev
|
26
26
|
choppy( :row => row - 1 )
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
# return the attribute of the underlying entity corresponding
|
30
30
|
# to the column of this index
|
31
31
|
def attribute
|
32
32
|
model.attributes[column]
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
# returns the list of ModelColumn metadata
|
36
36
|
def meta
|
37
37
|
# use the optimised version
|
38
38
|
# TODO just use the model version instead?
|
39
39
|
field.meta
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
42
|
# return the table's field name. For associations, this would
|
43
43
|
# be suffixed with _id
|
44
44
|
def field_name
|
45
45
|
meta.name
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
# return the value of the field, it may be the _id value
|
49
49
|
def field_value
|
50
50
|
entity.send( field_name )
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
53
|
# the underlying entity
|
54
54
|
# TODO caching doesn't help with Qt because ModelIndex objects are
|
55
55
|
# extremely short-lived. Not sure about swing.
|
@@ -57,9 +57,9 @@ module Clevic
|
|
57
57
|
return nil if model.nil?
|
58
58
|
model.collection[row]
|
59
59
|
end
|
60
|
-
|
60
|
+
|
61
61
|
attr_writer :entity
|
62
|
-
|
62
|
+
|
63
63
|
# return true if validation failed for this indexes field
|
64
64
|
def has_errors?
|
65
65
|
# virtual fields don't have metadata
|
@@ -69,14 +69,14 @@ module Clevic
|
|
69
69
|
entity.errors.invalid?( field_name.to_sym )
|
70
70
|
end
|
71
71
|
end
|
72
|
-
|
72
|
+
|
73
73
|
# return a collection of errors. Unlike AR, this
|
74
74
|
# will always return an array that will have zero, one
|
75
75
|
# or many elements.
|
76
76
|
def errors
|
77
77
|
[ entity.errors[field_name.to_sym] ].flatten
|
78
78
|
end
|
79
|
-
|
79
|
+
|
80
80
|
# sort by row, then column
|
81
81
|
def <=>( other )
|
82
82
|
row_comp = self.row <=> other.row
|
@@ -86,11 +86,11 @@ module Clevic
|
|
86
86
|
row_comp
|
87
87
|
end
|
88
88
|
end
|
89
|
-
|
89
|
+
|
90
90
|
def inspect
|
91
91
|
"#<TableIndex (#{row},#{column}) '#{raw_value rescue "no raw value: #{$!.message}"}'>"
|
92
92
|
end
|
93
|
-
|
93
|
+
|
94
94
|
# return a string (row,column)
|
95
95
|
# suitable for displaying to users, ie 1-based not 0-based
|
96
96
|
def rc
|
data/lib/clevic/table_model.rb
CHANGED
@@ -15,10 +15,10 @@ class TableModel
|
|
15
15
|
# the CacheTable of Clevic::Record or Sequel::Model objects
|
16
16
|
attr_reader :collection
|
17
17
|
alias_method :cache_table, :collection
|
18
|
-
|
18
|
+
|
19
19
|
# the collection of Clevic::Field objects
|
20
20
|
attr_reader :fields
|
21
|
-
|
21
|
+
|
22
22
|
attr_accessor :read_only
|
23
23
|
def read_only?; read_only; end
|
24
24
|
|
@@ -26,69 +26,69 @@ class TableModel
|
|
26
26
|
attr_accessor :auto_new
|
27
27
|
def auto_new?; auto_new; end
|
28
28
|
def auto_new?; auto_new; end
|
29
|
-
|
29
|
+
|
30
30
|
attr_accessor :entity_view
|
31
31
|
attr_accessor :builder
|
32
|
-
|
32
|
+
|
33
33
|
# If somre or all the entities in the collection is related to a single entity
|
34
34
|
# somewhere else, this is it. Not sure if this is the right
|
35
35
|
# way to do it, but try anyway.
|
36
36
|
attr_accessor :one
|
37
|
-
|
37
|
+
|
38
38
|
def entity_class
|
39
39
|
entity_view.entity_class
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
42
|
def fields=( arr )
|
43
43
|
@fields = arr
|
44
|
-
|
44
|
+
|
45
45
|
# reset these
|
46
46
|
@labels = nil
|
47
47
|
@attributes = nil
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
50
|
# field is a symbol or string referring to a column.
|
51
51
|
# returns the index of that field.
|
52
52
|
def field_column( field )
|
53
53
|
fields.each_with_index {|x,i| return i if x.id == field.to_sym }
|
54
54
|
end
|
55
|
-
|
55
|
+
|
56
56
|
def field_for_index( model_index )
|
57
57
|
fields[model_index.column]
|
58
58
|
end
|
59
|
-
|
59
|
+
|
60
60
|
def labels
|
61
61
|
@labels ||= fields.map {|x| x.label }
|
62
62
|
end
|
63
|
-
|
63
|
+
|
64
64
|
def attributes
|
65
65
|
@attributes ||= fields.map {|x| x.attribute }
|
66
66
|
end
|
67
|
-
|
67
|
+
|
68
68
|
def collection=( arr )
|
69
69
|
@collection = arr
|
70
70
|
# fill in an empty record for data entry
|
71
71
|
add_new_item if collection.empty? && auto_new?
|
72
72
|
end
|
73
|
-
|
73
|
+
|
74
74
|
# add a new item, and set defaults from the Clevic::View
|
75
75
|
# add_new_item_start and add_new_item_end are provided by
|
76
76
|
# the GUI framework-specific class
|
77
77
|
def add_new_item
|
78
78
|
add_new_item_start
|
79
|
-
|
79
|
+
|
80
80
|
entity = entity_class.new
|
81
|
-
|
81
|
+
|
82
82
|
# set default values without triggering changed
|
83
83
|
fields.each do |f|
|
84
84
|
f.set_default_for( entity ) unless f.default.nil?
|
85
85
|
end
|
86
|
-
|
86
|
+
|
87
87
|
collection << entity
|
88
|
-
|
88
|
+
|
89
89
|
add_new_item_end
|
90
90
|
end
|
91
|
-
|
91
|
+
|
92
92
|
# rows is a collection of integers specifying row indices to remove
|
93
93
|
def remove_rows( rows )
|
94
94
|
# don't delete rows twice
|
@@ -99,25 +99,25 @@ class TableModel
|
|
99
99
|
removals = rows_in_order.map do |index|
|
100
100
|
collection[index]
|
101
101
|
end
|
102
|
-
|
102
|
+
|
103
103
|
remove_notify( rows_in_order ) do
|
104
104
|
removals.each do |to_remove, index|
|
105
105
|
# destroy the db object, and its associated table row
|
106
106
|
to_remove.destroy unless to_remove.nil? || to_remove.new?
|
107
107
|
end
|
108
108
|
end
|
109
|
-
|
109
|
+
|
110
110
|
# because it's too bloody complicated to figure out which items
|
111
111
|
# were deleted and then remove them from the collection. And there
|
112
112
|
# most likely isn't a big hit for doing this, unless there's a lot
|
113
113
|
# of cached calcuation in the entities.
|
114
114
|
self.collection = collection.renew
|
115
|
-
|
115
|
+
|
116
116
|
# create a new row if auto_new is on
|
117
117
|
# should really be in a signal handler
|
118
118
|
add_new_item if collection.empty? && auto_new?
|
119
119
|
end
|
120
|
-
|
120
|
+
|
121
121
|
# save the model at the given index, if it's dirty
|
122
122
|
# TODO Sequel uses modified?
|
123
123
|
def save( index )
|
@@ -141,7 +141,7 @@ class TableModel
|
|
141
141
|
puts $!.backtrace
|
142
142
|
emit_data_error( index, nil, $!.message )
|
143
143
|
end
|
144
|
-
|
144
|
+
|
145
145
|
def reload_data( dataset = nil, &dataset_block )
|
146
146
|
# renew cache. All records will be dropped and reloaded.
|
147
147
|
self.collection = self.cache_table.renew( dataset, &dataset_block )
|
@@ -159,7 +159,7 @@ class TableModel
|
|
159
159
|
start_index.field
|
160
160
|
)
|
161
161
|
entity = searcher.search( start_index.entity )
|
162
|
-
|
162
|
+
|
163
163
|
# return matched indexes
|
164
164
|
unless entity.nil?
|
165
165
|
found_row = collection.index_for_entity( entity )
|
@@ -169,12 +169,12 @@ class TableModel
|
|
169
169
|
[]
|
170
170
|
end
|
171
171
|
end
|
172
|
-
|
172
|
+
|
173
173
|
class DataChange
|
174
174
|
class ModelIndexProxy
|
175
175
|
attr_accessor :row
|
176
176
|
attr_accessor :column
|
177
|
-
|
177
|
+
|
178
178
|
def initialize( other = nil )
|
179
179
|
unless other.nil?
|
180
180
|
@row = other.row
|
@@ -182,18 +182,18 @@ class TableModel
|
|
182
182
|
end
|
183
183
|
end
|
184
184
|
end
|
185
|
-
|
185
|
+
|
186
186
|
def top_left
|
187
187
|
@top_left ||= ModelIndexProxy.new
|
188
188
|
end
|
189
|
-
|
189
|
+
|
190
190
|
def bottom_right
|
191
191
|
@bottom_right ||= ModelIndexProxy.new
|
192
192
|
end
|
193
|
-
|
193
|
+
|
194
194
|
attr_writer :bottom_right
|
195
195
|
attr_writer :top_left
|
196
|
-
|
196
|
+
|
197
197
|
attr_reader :index
|
198
198
|
def index=( other )
|
199
199
|
self.top_left = ModelIndexProxy.new( other )
|
@@ -11,20 +11,20 @@ the matching record next after this.
|
|
11
11
|
class TableSearcher
|
12
12
|
include OrderedDataset
|
13
13
|
attr_reader :search_criteria, :field
|
14
|
-
|
14
|
+
|
15
15
|
# dataset is a Sequel::Dataset, which has an associated Sequel::Model
|
16
16
|
# field is an instance of Clevic::Field
|
17
17
|
# search_criteria responds to from_start?, direction, whole_words? and search_text
|
18
18
|
def initialize( dataset, search_criteria, field )
|
19
19
|
raise "field must be specified" if field.nil?
|
20
20
|
raise "unknown order #{search_criteria.direction}" unless [:forwards, :backwards].include?( search_criteria.direction )
|
21
|
-
|
21
|
+
|
22
22
|
self.dataset = dataset
|
23
|
-
|
23
|
+
|
24
24
|
@search_criteria = search_criteria
|
25
25
|
@field = field
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
# start_entity is the entity to start from, ie any record found after it will qualify
|
29
29
|
# return the first entity found that matches the criteria
|
30
30
|
def search( start_entity = nil )
|
@@ -36,12 +36,12 @@ protected
|
|
36
36
|
def search_field_expression
|
37
37
|
if field.association?
|
38
38
|
raise "display not specified for #{field}" if field.display.nil?
|
39
|
-
|
39
|
+
|
40
40
|
# for related table displays as procs
|
41
41
|
unless [String,Symbol].include?( field.display.class )
|
42
42
|
raise( "search field #{field.inspect} cannot search lambda display" )
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
# TODO this will only work with a path value with no dots
|
46
46
|
# otherwise the SQL gets complicated with joins etc
|
47
47
|
field.related_class \
|
@@ -52,7 +52,7 @@ protected
|
|
52
52
|
field.attribute.to_sym
|
53
53
|
end
|
54
54
|
end
|
55
|
-
|
55
|
+
|
56
56
|
# return an expression, or an array or expressions for representing search_criteria.search_text and whole_words?
|
57
57
|
def search_text_expression
|
58
58
|
if search_criteria.whole_words?
|
@@ -66,7 +66,7 @@ protected
|
|
66
66
|
"%#{search_criteria.search_text}%"
|
67
67
|
end
|
68
68
|
end
|
69
|
-
|
69
|
+
|
70
70
|
# Add the relevant conditions to use start_entity as the
|
71
71
|
# entity where the search starts, ie the first one after it is found
|
72
72
|
# start_entity is a model instance
|
@@ -76,12 +76,12 @@ protected
|
|
76
76
|
# pure boolean expression - they need something to compare it to.
|
77
77
|
dataset.filter( expression => true )
|
78
78
|
end
|
79
|
-
|
79
|
+
|
80
80
|
# return a dataset based on dataset which filters on search_criteria
|
81
81
|
def search_dataset( start_entity )
|
82
82
|
likes = Array[*search_text_expression].map{|ste| Sequel::SQL::StringExpression.like(search_field_expression, ste, {:case_insensitive=>true})}
|
83
83
|
rv = dataset.filter( Sequel::SQL::BooleanExpression.new(:OR, *likes ) )
|
84
|
-
|
84
|
+
|
85
85
|
# if we're not searching from the start, we need
|
86
86
|
# to find the next match. Which is complicated from an SQL point of view.
|
87
87
|
unless search_criteria.from_start?
|
@@ -89,25 +89,25 @@ protected
|
|
89
89
|
# build up the ordering conditions
|
90
90
|
rv = find_from( rv, start_entity )
|
91
91
|
end
|
92
|
-
|
92
|
+
|
93
93
|
# reverse order by direction if necessary
|
94
94
|
rv = rv.reverse if search_criteria.direction == :backwards
|
95
|
-
|
95
|
+
|
96
96
|
# return dataset
|
97
97
|
rv
|
98
98
|
end
|
99
|
-
|
99
|
+
|
100
100
|
# recursively create a case statement to do the comparison
|
101
101
|
# because and ... and ... and filters on *each* one rather than
|
102
102
|
# consecutively.
|
103
103
|
def build_recursive_comparison( start_entity, index = 0 )
|
104
104
|
# end recursion
|
105
105
|
return false if index == order_attributes.size
|
106
|
-
|
106
|
+
|
107
107
|
# fetch the current order attribute and direction
|
108
108
|
attribute, direction = order_attributes[index]
|
109
109
|
value = start_entity.send( attribute )
|
110
|
-
|
110
|
+
|
111
111
|
# build case statement using Sequel expressions, including recursion
|
112
112
|
# pseudo-SQL is
|
113
113
|
# case
|
@@ -115,7 +115,7 @@ protected
|
|
115
115
|
# when attribute = value then #{build_recursive_comparison( operator, index+1 )}
|
116
116
|
# else false
|
117
117
|
# end
|
118
|
-
|
118
|
+
|
119
119
|
{
|
120
120
|
# if values are unequal, comparison levels end here
|
121
121
|
attribute.identifier.send( comparator(direction), value ) => true,
|
@@ -123,7 +123,7 @@ protected
|
|
123
123
|
{ attribute => value } => build_recursive_comparison( start_entity, index+1 )
|
124
124
|
}.case( false ) # the else (default) clause, ie we don't want to see these records
|
125
125
|
end
|
126
|
-
|
126
|
+
|
127
127
|
# return either > or < depending on both search_criteria.direction
|
128
128
|
# and local_direction
|
129
129
|
def comparator( local_direction = 1 )
|
@@ -132,11 +132,11 @@ protected
|
|
132
132
|
when :forwards; 1
|
133
133
|
when :backwards; -1
|
134
134
|
end * local_direction
|
135
|
-
|
135
|
+
|
136
136
|
# 1 indexes >, -1 indexes <
|
137
137
|
['','>','<'][comparator_direction]
|
138
138
|
end
|
139
|
-
|
139
|
+
|
140
140
|
end
|
141
141
|
|
142
142
|
end
|