1
1
require 'glimmer-dsl-libui'
2
2
3
- # TODO refactor to follow MVP correctly by extracting a presenter or extra model
4
- class FormTable
5
- Contact = Struct . new ( :name , :email , :phone , :city , :state )
6
-
7
- include Glimmer
3
+ Contact = Struct . new ( :name , :email , :phone , :city , :state ) do
4
+ def valid?
5
+ values . map ( &:to_s ) . any? ( &:empty? )
6
+ end
8
7
9
- attr_accessor :contacts , :name , :email , :phone , :city , :state , :filter_value
8
+ def reset
9
+ self . name = ''
10
+ self . email = ''
11
+ self . phone = ''
12
+ self . city = ''
13
+ self . state = ''
14
+ end
15
+ end
16
+
17
+ class FormTablePresenter
18
+ attr_accessor :contacts , :filter_value , :unfiltered_contacts
10
19
11
20
def initialize
12
21
@contacts = [
@@ -18,6 +27,38 @@ def initialize
18
27
]
19
28
end
20
29
30
+ def new_contact
31
+ @new_contact ||= Contact . new
32
+ end
33
+
34
+ def save_contact
35
+ contacts << new_contact . dup # automatically inserts a row into the table due to explicit data-binding
36
+ self . unfiltered_contacts = contacts . dup
37
+ new_contact . reset # automatically clears form fields through explicit data-binding
38
+ end
39
+
40
+ def filter_table
41
+ self . unfiltered_contacts ||= contacts . dup
42
+ # Unfilter first to remove any previous filters
43
+ self . contacts = unfiltered_contacts . dup # affects table indirectly through explicit data-binding
44
+ # Now, apply filter if entered
45
+ unless filter_value . empty?
46
+ self . contacts = contacts . filter do |contact | # affects table indirectly through explicit data-binding
47
+ contact . members . any? do |attribute |
48
+ contact [ attribute ] . to_s . downcase . include? ( filter_value . downcase )
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ class FormTable
56
+ include Glimmer
57
+
58
+ def initialize
59
+ @presenter = FormTablePresenter . new
60
+ end
61
+
21
62
def launch
22
63
window ( 'Contacts' , 600 , 600 ) {
23
64
margined true
@@ -28,67 +69,48 @@ def launch
28
69
29
70
entry {
30
71
label 'Name'
31
- text <=> [ self , :name ] # bidirectional data-binding between entry text and self .name
72
+ text <=> [ @presenter . new_contact , :name ] # bidirectional data-binding between entry text and @presenter .name
32
73
}
33
74
34
75
entry {
35
76
label 'Email'
36
- text <=> [ self , :email ]
77
+ text <=> [ @presenter . new_contact , :email ]
37
78
}
38
79
39
80
entry {
40
81
label 'Phone'
41
- text <=> [ self , :phone ]
82
+ text <=> [ @presenter . new_contact , :phone ]
42
83
}
43
84
44
85
entry {
45
86
label 'City'
46
- text <=> [ self , :city ]
87
+ text <=> [ @presenter . new_contact , :city ]
47
88
}
48
89
49
90
entry {
50
91
label 'State'
51
- text <=> [ self , :state ]
92
+ text <=> [ @presenter . new_contact , :state ]
52
93
}
53
94
}
54
95
55
96
button ( 'Save Contact' ) {
56
97
stretchy false
57
98
58
99
on_clicked do
59
- new_row = [ name , email , phone , city , state ]
60
- if new_row . map ( &:to_s ) . include? ( '' )
100
+ if @presenter . new_contact . valid?
61
101
msg_box_error ( 'Validation Error!' , 'All fields are required! Please make sure to enter a value for all fields.' )
62
102
else
63
- @contacts << Contact . new ( *new_row ) # automatically inserts a row into the table due to explicit data-binding
64
- @unfiltered_contacts = @contacts . dup
65
- self . name = '' # automatically clears name entry through explicit data-binding
66
- self . email = ''
67
- self . phone = ''
68
- self . city = ''
69
- self . state = ''
103
+ @presenter . save_contact
70
104
end
71
105
end
72
106
}
73
107
74
108
search_entry {
75
109
stretchy false
76
- # bidirectional data-binding of text to self.filter_value with after_write option
77
- text <=> [ self , :filter_value ,
78
- after_write : -> ( filter_value ) { # execute after write to self.filter_value
79
- @unfiltered_contacts ||= @contacts . dup
80
- # Unfilter first to remove any previous filters
81
- self . contacts = @unfiltered_contacts . dup # affects table indirectly through explicit data-binding
82
- # Now, apply filter if entered
83
- unless filter_value . empty?
84
- self . contacts = @contacts . filter do |contact | # affects table indirectly through explicit data-binding
85
- contact . members . any? do |attribute |
86
- contact [ attribute ] . to_s . downcase . include? ( filter_value . downcase )
87
- end
88
- end
89
- end
90
- }
91
- ]
110
+ # bidirectional data-binding of text to @presenter.filter_value, filtering table after writing value to Model
111
+ text <=> [ @presenter , :filter_value ,
112
+ after_write : -> ( filter_value ) { @presenter . filter_table }
113
+ ]
92
114
}
93
115
94
116
table {
@@ -99,7 +121,7 @@ def launch
99
121
text_column ( 'State' )
100
122
101
123
editable true
102
- cell_rows <=> [ self , :contacts ] # explicit data-binding to self .contacts Model Array, auto-inferring model attribute names from underscored table column names by convention
124
+ cell_rows <=> [ @presenter , :contacts ] # explicit data-binding to @presenter .contacts Model Array, auto-inferring model attribute names from underscored table column names by convention
103
125
104
126
on_changed do |row , type , row_data |
105
127
puts "Row #{ row } #{ type } : #{ row_data } "
0 commit comments