xrb-formatters 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7c7d3c4882fa5d1766266d11516647193f2edb6a414aa3901cd2280da98b219e
4
+ data.tar.gz: 82609011a18a1ebf7848c8b92654b75ed9721d2f27640b2687fdb4d89e03cec7
5
+ SHA512:
6
+ metadata.gz: af17a0f56086a82b143d69d5b02ec070126a8182bd1a6ecb8ce628d10b6063ac6a689e15d69c059e298128202165c9b15ed382ce27f7147a387bb5eb7933bf99
7
+ data.tar.gz: 836b7440bf8623edf869c929172c32c4613a89c6ff18b68aff2a3f26fc68bd62da3e2f0d0a123940f6bf28051b3d1aac54f6d0083988b01e36887041ba368e9d
checksums.yaml.gz.sig ADDED
@@ -0,0 +1 @@
1
+ <���3S-q�Aa�G�"hsiǯ���S����g^^]C_�1���v�������XT����TZܼXc������� �c�Hr��'~&� $aK�廰��L���t������!y����Г<�O�b�_lgu�i�NX�S�n�=���Eh<�Lv�8x`V��{gP����5
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2012-2024, by Samuel Williams.
5
+
6
+ require 'xrb/strings'
7
+ require 'mapping/model'
8
+ require 'mapping/descendants'
9
+
10
+ module XRB
11
+ module Formatters
12
+ class Formatter < Mapping::Model
13
+ def self.for(object, **options)
14
+ self.new(object: object, **options)
15
+ end
16
+
17
+ def initialize(**options)
18
+ @options = options
19
+
20
+ @object = nil
21
+ end
22
+
23
+ # The target object of the form.
24
+ def object
25
+ @object ||= @options[:object]
26
+ end
27
+
28
+ def nested_name(**options)
29
+ options[:nested_name] || @options[:nested_name]
30
+ end
31
+
32
+ # The name of the field, used for the name attribute of an input.
33
+ def name_for(**options)
34
+ name = options[:name] || options[:field]
35
+
36
+ if suffix = options[:suffix]
37
+ name = "#{name}#{suffix}"
38
+ end
39
+
40
+ if nested_name = self.nested_name(**options)
41
+ "#{nested_name}[#{name}]"
42
+ else
43
+ name
44
+ end
45
+ end
46
+
47
+ def nested_name_for(**options)
48
+ name_for(**options)
49
+ end
50
+
51
+ def nested(name, key = name, klass: self.class)
52
+ options = @options.dup
53
+ target = self.object.send(name)
54
+
55
+ options[:object] = target
56
+ options[:nested_name] = nested_name_for(name: key)
57
+
58
+ formatter = klass.new(**options)
59
+
60
+ return formatter unless block_given?
61
+
62
+ yield formatter
63
+ end
64
+
65
+ attr :options
66
+
67
+ def format_unspecified(object, **options)
68
+ object.to_s
69
+ end
70
+
71
+ def format(object, **options)
72
+ method_name = self.method_for_mapping(object)
73
+
74
+ if self.respond_to?(method_name)
75
+ self.send(method_name, object, **options)
76
+ else
77
+ format_unspecified(object, **options)
78
+ end
79
+ end
80
+
81
+ alias text format
82
+
83
+ def [] key
84
+ @options[key]
85
+ end
86
+
87
+ map(String) do |object, **options|
88
+ object
89
+ end
90
+
91
+ map(NilClass) do |object, **options|
92
+ options[:blank] || @options[:blank] || ""
93
+ end
94
+
95
+ map(TrueClass, FalseClass, *Mapping.lookup_descendants(Numeric)) do |object, **options|
96
+ object.to_s
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2020-2024, by Samuel Williams.
5
+
6
+ require 'xrb/builder'
7
+
8
+ module XRB
9
+ module Formatters
10
+ module HTML
11
+ class AcceptCheckbox
12
+ def self.call(formatter, builder, **options, &block)
13
+ instance = self.new(formatter, builder, **options)
14
+
15
+ instance.call(&block)
16
+ end
17
+
18
+ def initialize(formatter, builder, **options)
19
+ @formatter = formatter
20
+ @builder = builder
21
+ @options = options
22
+ end
23
+
24
+ def name_for(**options)
25
+ @formatter.name_for(**options)
26
+ end
27
+
28
+ def checkbox_attributes_for(**options)
29
+ @formatter.checkbox_attributes_for(**options)
30
+ end
31
+
32
+ def call(&block)
33
+ Builder.fragment(@builder) do |builder|
34
+ builder.inline('span') do
35
+ builder.inline :input, type: :hidden, name: name_for(**@options), value: 'false'
36
+
37
+ builder.tag :input, checkbox_attributes_for(**@options)
38
+
39
+ builder.text " "
40
+
41
+ builder.capture(self, &block)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2012-2024, by Samuel Williams.
5
+
6
+ require 'xrb/builder'
7
+ require 'xrb/template'
8
+
9
+ require_relative 'form_formatter'
10
+
11
+ module XRB
12
+ module Formatters
13
+ module HTML
14
+ module DefinitionListForm
15
+ include FormFormatter
16
+
17
+ # An input field (single line text).
18
+ def input(**options)
19
+ options = @options.merge(**options)
20
+
21
+ Builder.fragment do |builder|
22
+ builder.inline(:dt) do
23
+ builder.text title_for(**options)
24
+ end
25
+
26
+ builder.inline(:dd) do
27
+ builder.tag :input, input_attributes_for(**options)
28
+
29
+ if details = details_for(**options)
30
+ builder.inline(:small, class: 'details') {builder.text details}
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ # An output field for the result of a computation.
37
+ def output(**options)
38
+ options = @options.merge(**options)
39
+
40
+ Builder.fragment do |builder|
41
+ builder.inline(:dt) {builder.text title_for(**options)}
42
+
43
+ builder.inline(:dd) do
44
+ builder.inline :output, output_attributes_for(**options) do
45
+ builder.text value_for(**options)
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ # A textarea field (multi-line text).
52
+ def textarea(**options)
53
+ options = @options.merge(**options)
54
+
55
+ Builder.fragment do |builder|
56
+ builder.tag(:dt) do
57
+ builder.text title_for(**options)
58
+
59
+ if details = details_for(**options)
60
+ builder.inline(:small, class: 'details') {builder.text details}
61
+ end
62
+ end
63
+
64
+ builder.inline(:dd) do
65
+ builder.tag :textarea, textarea_attributes_for(**options) do
66
+ builder.text value_for(**options)
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ # A checkbox field.
73
+ def checkbox(**options)
74
+ options = @options.merge(**options)
75
+
76
+ Builder.fragment do |builder|
77
+ builder.tag(:dd) do
78
+ builder.tag :input, :type => :hidden, :name => name_for(**options), :value => 'false'
79
+
80
+ builder.inline(:label) do
81
+ builder.tag :input, checkbox_attributes_for(**options)
82
+ # We would like a little bit of whitespace between the checkbox and the title.
83
+ builder.text " " + title_for(**options)
84
+ end
85
+
86
+ if details = details_for(**options)
87
+ builder.inline(:small, class: 'details') {builder.text details}
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ # A submission button
94
+ def submit(**options)
95
+ options = @options.merge(**options)
96
+ options[:title] ||= submit_title_for(**options)
97
+
98
+ Builder.fragment do |builder|
99
+ builder.tag :input, submit_attributes_for(**options)
100
+ end
101
+ end
102
+
103
+ def element(klass, **options, &block)
104
+ options = @options.merge(**options)
105
+
106
+ Builder.fragment(block&.binding) do |builder|
107
+ builder.inline(:dt) do
108
+ builder.text title_for(**options)
109
+ end
110
+
111
+ builder.tag(:dd) do
112
+ klass.call(self, builder, **options, &block)
113
+
114
+ if details = details_for(**options)
115
+ builder.inline(:small, class: 'details') {builder.text details}
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ def fieldset(**options, &block)
122
+ super do |builder|
123
+ builder.tag(:dl) do
124
+ yield(builder)
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,222 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2014-2024, by Samuel Williams.
5
+
6
+ require 'xrb/builder'
7
+
8
+ module XRB
9
+ module Formatters
10
+ module HTML
11
+ module FormFormatter
12
+ # Return true if the object is begin created or false if it is being updated.
13
+ def new_record?
14
+ object.new_record?
15
+ end
16
+
17
+ # Any additional details relating to a field (e.g. explanation text)
18
+ def details_for(**options)
19
+ options[:details]
20
+ end
21
+
22
+ def field_for(**options)
23
+ options[:field]
24
+ end
25
+
26
+ # A title is a text string that will be displayed next to or on top of the control to describe it or its value:
27
+ def title_for(**options)
28
+ if title = options[:title]
29
+ return title
30
+ end
31
+
32
+ # Generate a title from a field name:
33
+ if field_name = field_for(**options)
34
+ # Remove postfix "_id" or "_ids":
35
+ return Strings::to_title(field_name.to_s.sub(/_ids?/, ''))
36
+ end
37
+ end
38
+
39
+ def object_value_for(**options)
40
+ if object = options[:object] and field = field_for(**options)
41
+ object.send(field)
42
+ end
43
+ end
44
+
45
+ def raw_value_for(**options)
46
+ value = options.fetch(:value) {object_value_for(**options)}
47
+
48
+ # Allow to specify a default value if the value given, usually from an object, is nil.
49
+ value || options[:default]
50
+ end
51
+
52
+ # The value of the field.
53
+ def value_for(**options)
54
+ if value = raw_value_for(**options)
55
+ self.format(value, **options)
56
+ end
57
+ end
58
+
59
+ def pattern_for(**options)
60
+ options[:pattern]
61
+ end
62
+
63
+ def placeholder_for(**options)
64
+ options[:placeholder]
65
+ end
66
+
67
+ def input_attributes_for(**options)
68
+ attributes = {
69
+ :type => options[:type],
70
+ :name => name_for(**options),
71
+ :id => options[:id],
72
+ :class => options[:class],
73
+ :value => value_for(**options),
74
+ :required => options[:required],
75
+ :disabled => options[:disabled],
76
+ :readonly => options[:readonly],
77
+ :pattern => pattern_for(**options),
78
+ :placeholder => placeholder_for(**options),
79
+ # for <input type="range|number">
80
+ :min => options[:minimum] || options[:min],
81
+ :max => options[:maximum] || options[:max],
82
+ :step => options[:step],
83
+ # for <input type="text">
84
+ :minlength => options[:minimum] || options[:minlength],
85
+ :maxlength => options[:maximum] || options[:maxlength],
86
+ :data => options[:data],
87
+ }
88
+
89
+ return attributes
90
+ end
91
+
92
+ def output_attributes_for(**options)
93
+ attributes = {
94
+ :name => name_for(**options),
95
+ :id => options[:id],
96
+ :class => options[:class],
97
+ :for => options[:for],
98
+ :form => options[:form],
99
+ :data => options[:data],
100
+ }
101
+
102
+ return attributes
103
+ end
104
+
105
+ def textarea_attributes_for(**options)
106
+ return {
107
+ :name => name_for(**options),
108
+ :id => options[:id],
109
+ :class => options[:class],
110
+ :required => options[:required],
111
+ :disabled => options[:disabled],
112
+ :readonly => options[:readonly],
113
+ :pattern => pattern_for(**options),
114
+ :placeholder => placeholder_for(**options),
115
+ :minlength => options[:minlength],
116
+ :maxlength => options[:maxlength],
117
+ :data => options[:data],
118
+ }
119
+ end
120
+
121
+ def checkbox_attributes_for(**options)
122
+ return {
123
+ :type => options[:type] || 'checkbox',
124
+ :id => options[:id],
125
+ :class => options[:class],
126
+ :name => name_for(**options),
127
+ :value => 'true',
128
+ :checked => raw_value_for(**options),
129
+ :required => options[:required],
130
+ :disabled => options[:disabled],
131
+ :readonly => options[:readonly],
132
+ :data => options[:data],
133
+ }
134
+ end
135
+
136
+ def submit_attributes_for(**options)
137
+ return {
138
+ :type => options[:type] || 'submit',
139
+ :name => name_for(**options),
140
+ :id => options[:id],
141
+ :class => options[:class],
142
+ :disabled => options[:disabled],
143
+ :value => title_for(**options),
144
+ :data => options[:data],
145
+ }
146
+ end
147
+
148
+ def submit_title_for(**options)
149
+ title_for(**options) || (new_record? ? 'Create' : 'Update')
150
+ end
151
+
152
+ def hidden_attributes_for(**options)
153
+ return {
154
+ :type => options[:type] || 'hidden',
155
+ :id => options[:id],
156
+ :class => options[:class],
157
+ :name => name_for(**options),
158
+ :value => value_for(**options),
159
+ :data => options[:data],
160
+ }
161
+ end
162
+
163
+ # A hidden field.
164
+ def hidden(**options)
165
+ options = @options.merge(**options)
166
+
167
+ Builder.fragment do |builder|
168
+ builder.tag :input, hidden_attributes_for(**options)
169
+ end
170
+ end
171
+
172
+ def button_attributes_for(**options)
173
+ return {
174
+ :type => options[:type] || 'submit',
175
+ :name => name_for(**options),
176
+ :id => options[:id],
177
+ :class => options[:class],
178
+ :disabled => options[:disabled],
179
+ :value => value_for(**options),
180
+ :data => options[:data],
181
+ }
182
+ end
183
+
184
+ def button_title_for(**options)
185
+ type = options.fetch(:type, 'submit').to_sym
186
+
187
+ if type == :submit
188
+ submit_title_for(**options)
189
+ else
190
+ title_for(**options) || Strings::to_title(type.to_s)
191
+ end
192
+ end
193
+
194
+ # A hidden field.
195
+ def button(**options)
196
+ options = @options.merge(**options)
197
+
198
+ Builder.fragment do |builder|
199
+ builder.inline :button, button_attributes_for(**options) do
200
+ builder.text button_title_for(**options)
201
+ end
202
+ end
203
+ end
204
+
205
+ def fieldset(**options, &block)
206
+ options = @options.merge(**options)
207
+ buffer = XRB::Template.buffer(block.binding)
208
+
209
+ Builder.fragment(buffer) do |builder|
210
+ builder.tag('fieldset') do
211
+ builder.inline('legend') do
212
+ builder.text title_for(**options)
213
+ end
214
+
215
+ yield(builder)
216
+ end
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end
222
+ end
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2020-2024, by Samuel Williams.
5
+
6
+ require 'xrb/builder'
7
+ require 'xrb/template'
8
+
9
+ require_relative 'form_formatter'
10
+
11
+ module XRB
12
+ module Formatters
13
+ module HTML
14
+ module LabelForm
15
+ include FormFormatter
16
+
17
+ # An input field (single line text).
18
+ def input(**options)
19
+ options = @options.merge(**options)
20
+
21
+ Builder.fragment do |builder|
22
+ builder.inline(:label) do
23
+ builder.inline(:span) do
24
+ builder.text title_for(**options)
25
+
26
+ if details = details_for(**options)
27
+ builder.inline(:small) {builder.text details}
28
+ end
29
+ end
30
+
31
+ builder.inline :input, input_attributes_for(**options)
32
+ end
33
+ end
34
+ end
35
+
36
+ # An output field for the result of a computation.
37
+ def output(**options)
38
+ options = @options.merge(**options)
39
+
40
+ builder.inline(:label) do
41
+ builder.inline(:span) do
42
+ builder.text title_for(**options)
43
+
44
+ if details = details_for(**options)
45
+ builder.inline(:small) {builder.text details}
46
+ end
47
+ end
48
+
49
+ builder.inline :output, output_attributes_for(**options) do
50
+ builder.text value_for(**options)
51
+ end
52
+ end
53
+ end
54
+
55
+ # A textarea field (multi-line text).
56
+ def textarea(**options)
57
+ options = @options.merge(**options)
58
+
59
+ Builder.fragment do |builder|
60
+ builder.inline(:label) do
61
+ builder.inline(:span) do
62
+ builder.text title_for(**options)
63
+
64
+ if details = details_for(**options)
65
+ builder.inline(:small) {builder.text details}
66
+ end
67
+ end
68
+
69
+ builder.tag :textarea, textarea_attributes_for(**options) do
70
+ builder.text value_for(**options)
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ # A checkbox field.
77
+ def checkbox(**options)
78
+ options = @options.merge(**options)
79
+
80
+ Builder.fragment do |builder|
81
+ builder.inline(:label) do
82
+ builder.inline :input, :type => :hidden, :name => name_for(**options), :value => 'false'
83
+
84
+ builder.inline(:span) do
85
+ if details = details_for(**options)
86
+ builder.inline(:small) {builder.text details}
87
+ end
88
+ end
89
+
90
+ builder.tag :input, checkbox_attributes_for(**options)
91
+
92
+ # We would like a little bit of whitespace between the checkbox and the title:
93
+ builder.text " " + title_for(**options)
94
+ end
95
+ end
96
+ end
97
+
98
+ # A submission button
99
+ def submit(**options)
100
+ options = @options.merge(**options)
101
+ options[:title] ||= submit_title_for(**options)
102
+
103
+ Builder.fragment do |builder|
104
+ builder.inline :input, submit_attributes_for(**options)
105
+ end
106
+ end
107
+
108
+ def element(klass, **options, &block)
109
+ options = @options.merge(**options)
110
+ buffer = XRB::Template.buffer(block.binding)
111
+
112
+ Builder.fragment(buffer) do |builder|
113
+ builder.inline(:label) do
114
+ builder.inline(:span) do
115
+ builder.text title_for(**options)
116
+
117
+ if details = details_for(**options)
118
+ builder.inline(:small) {builder.text details}
119
+ end
120
+ end
121
+
122
+ klass.call(self, builder, **options, &block)
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2012-2024, by Samuel Williams.
5
+
6
+ require 'xrb/builder'
7
+
8
+ module XRB
9
+ module Formatters
10
+ module HTML
11
+ # Standard drop-down select box:
12
+ class OptionSelect
13
+ def self.call(formatter, builder, **options, &block)
14
+ instance = self.new(formatter, builder, **options)
15
+
16
+ instance.call(&block)
17
+ end
18
+
19
+ def initialize(formatter, builder, **options)
20
+ @formatter = formatter
21
+ @builder = builder
22
+ @options = options
23
+ end
24
+
25
+ def name_for(**options)
26
+ if name = @formatter.name_for(**options)
27
+ if options[:multiple]
28
+ name = "#{name}[]"
29
+ end
30
+
31
+ return name
32
+ end
33
+ end
34
+
35
+ def raw_value_for(**options)
36
+ @formatter.raw_value_for(**options)
37
+ end
38
+
39
+ def raw_value
40
+ @raw_value ||= raw_value_for(**@options)
41
+ end
42
+
43
+ def value_for(**options)
44
+ @formatter.value_for(**options)
45
+ end
46
+
47
+ def title_for(**options)
48
+ @formatter.title_for(**options)
49
+ end
50
+
51
+ def option_attributes_for(**options)
52
+ return {
53
+ :value => value_for(**options),
54
+ :selected => options.fetch(:selected) {raw_value == raw_value_for(**options)},
55
+ :id => options[:id],
56
+ :class => options[:class],
57
+ :data => options[:data],
58
+ }
59
+ end
60
+
61
+ def item(**options, &block)
62
+ options[:field] ||= 'id'
63
+
64
+ Builder.fragment(block&.binding || @builder) do |builder|
65
+ builder.inline(:option, option_attributes_for(**options)) do
66
+ if block_given?
67
+ builder.capture(self, &block)
68
+ else
69
+ builder.text title_for(**options)
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ def optional_title_for(**options)
76
+ if options[:optional] == true
77
+ options[:blank] || ''
78
+ else
79
+ options[:optional]
80
+ end
81
+ end
82
+
83
+ def group_attributes_for(**options)
84
+ return {
85
+ :label => title_for(**options),
86
+ :id => options[:id],
87
+ :class => options[:class],
88
+ :data => options[:data],
89
+ }
90
+ end
91
+
92
+ def group(**options, &block)
93
+ @builder.tag :optgroup, group_attributes_for(**options) do
94
+ if options[:optional]
95
+ item(title: optional_title_for(**options), value: nil)
96
+ end
97
+
98
+ @builder.capture(&block)
99
+ end
100
+ end
101
+
102
+ def select_attributes_for(**options)
103
+ return {
104
+ :name => name_for(**options),
105
+ :id => options[:id],
106
+ :class => options[:class],
107
+ :multiple => options[:multiple],
108
+ :data => options[:data],
109
+ :required => options[:required],
110
+ }
111
+ end
112
+
113
+ def optional?
114
+ @options[:optional]
115
+ end
116
+
117
+ def call(&block)
118
+ Builder.fragment(@builder) do |builder|
119
+ builder.tag :select, select_attributes_for(**@options) do
120
+ if self.optional?
121
+ builder << item(title: optional_title_for(**@options), value: nil)
122
+ end
123
+
124
+ builder.capture(self, &block)
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2012-2024, by Samuel Williams.
5
+
6
+ require 'xrb/builder'
7
+
8
+ module XRB
9
+ module Formatters
10
+ module HTML
11
+ # Table based select boxes using per-row checkboxes.
12
+ class RadioSelect
13
+ def self.call(formatter, builder, **options, &block)
14
+ instance = self.new(formatter, builder, **options)
15
+
16
+ instance.call(&block)
17
+ end
18
+
19
+ def initialize(formatter, builder, **options)
20
+ @formatter = formatter
21
+ @builder = builder
22
+ @options = options
23
+
24
+ @field = options[:field]
25
+ end
26
+
27
+ def name_for(**options)
28
+ @formatter.name_for(**options)
29
+ end
30
+
31
+ def raw_value_for(**options)
32
+ @formatter.raw_value_for(**options)
33
+ end
34
+
35
+ def raw_value
36
+ @raw_value ||= raw_value_for(**@options)
37
+ end
38
+
39
+ def value_for(**options)
40
+ @formatter.value_for(**options)
41
+ end
42
+
43
+ def title_for(**options)
44
+ @formatter.title_for(**options)
45
+ end
46
+
47
+ def radio_attributes_for(**options)
48
+ return {
49
+ :type => :radio,
50
+ :name => @field,
51
+ # We set a default value to empty string, otherwise it becomes "on".
52
+ :value => value_for(**options) || "",
53
+ :checked => options.fetch(:selected) {raw_value == raw_value_for(**options)},
54
+ :data => options[:data],
55
+ }
56
+ end
57
+
58
+ def item(**options, &block)
59
+ Builder.fragment(block&.binding || @builder) do |builder|
60
+ builder.tag :tr do
61
+ builder.inline(:td, :class => :handle) do
62
+ builder.tag :input, radio_attributes_for(**options)
63
+ end
64
+
65
+ builder.inline(:td, :class => :item) do
66
+ if block_given?
67
+ builder.capture(self, &block)
68
+ else
69
+ builder.text title_for(**options)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ def optional_title_for(**options)
77
+ if options[:optional] == true
78
+ options[:blank] || ''
79
+ else
80
+ options[:optional]
81
+ end
82
+ end
83
+
84
+ def optional?
85
+ @options[:optional]
86
+ end
87
+
88
+ def call(&block)
89
+ Builder.fragment(@builder) do |builder|
90
+ builder.tag :table do
91
+ builder.tag :tbody do
92
+ if self.optional?
93
+ builder << item(title: optional_title_for(**@options), value: nil)
94
+ end
95
+
96
+ builder.capture(self, &block)
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2016-2024, by Samuel Williams.
5
+
6
+ require 'markly'
7
+
8
+ require 'xrb/markup'
9
+ require 'xrb/sanitize/fragment'
10
+
11
+ module XRB
12
+ module Formatters
13
+ module Markdown
14
+ def markdown(text, filter = XRB::Sanitize::Fragment, **options)
15
+ root = Markly.parse(text, **options)
16
+
17
+ html = filter.parse(root.to_html).output
18
+
19
+ return MarkupString.raw(html)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2016-2024, by Samuel Williams.
5
+
6
+ require 'xrb/strings'
7
+ require 'mapping/model'
8
+
9
+ module XRB
10
+ module Formatters
11
+ module RelativeTime
12
+ def self.included(base)
13
+ base.map(Time) do |object, **options|
14
+ current_time = options.fetch(:current_time) {Time.now}
15
+
16
+ # Ensure we display the time in localtime, and show the year if it is different:
17
+ if object.localtime.year != current_time.year
18
+ object.localtime.strftime("%B %-d, %-l:%M%P, %Y")
19
+ else
20
+ object.localtime.strftime("%B %-d, %-l:%M%P")
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2016-2024, by Samuel Williams.
5
+
6
+ require 'xrb/strings'
7
+ require 'mapping/model'
8
+
9
+ module XRB
10
+ module Formatters
11
+ module TruncatedText
12
+ def truncated_text(content, length: 30, **options)
13
+ if content
14
+ content = TruncatedText.truncate_text(content, length, **options)
15
+
16
+ return self.format(content)
17
+ end
18
+ end
19
+
20
+ def self.truncate_text(text, truncate_at, omission: nil, separator: nil, **options)
21
+ return text.dup unless text.length > truncate_at
22
+
23
+ omission ||= '...'
24
+
25
+ length_with_room_for_omission = truncate_at - omission.length
26
+
27
+ stop = nil
28
+
29
+ if separator
30
+ stop = text.rindex(separator, length_with_room_for_omission)
31
+ end
32
+
33
+ stop ||= length_with_room_for_omission
34
+
35
+ "#{text[0...stop]}#{omission}"
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2012-2024, by Samuel Williams.
5
+
6
+ module XRB
7
+ module Formatters
8
+ VERSION = "0.1.0"
9
+ end
10
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2012-2024, by Samuel Williams.
5
+
6
+ require 'xrb/formatters/formatter'
data/license.md ADDED
@@ -0,0 +1,21 @@
1
+ # MIT License
2
+
3
+ Copyright, 2012-2024, by Samuel Williams.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/readme.md ADDED
@@ -0,0 +1,63 @@
1
+ # XRB::Formatters
2
+
3
+ XRB is a templating system, and these formatters assist with the development
4
+ of typical view and form based web interface. A formatter is a high-level
5
+ adapter that turns model data into presentation text.
6
+
7
+ Formatters are designed to be customised, typically per-project, for specific
8
+ formatting needs.
9
+
10
+ [![Development Status](https://github.com/socketry/xrb-formatters/workflows/Test/badge.svg)](https://github.com/socketry/xrb-formatters/actions?workflow=Test)
11
+
12
+ ## Motivation
13
+
14
+ `XRB::Formatters` was a library extracted from [Financier](https://github.com/ioquatix/financier), an small business management app, itself, derived from an old Rails app. I was a bit envious of `form_for` in terms of the ease of generating forms, but found that it wasn't very extendable. I also found myself generating code to format model data as rich HTML. `XRB::Formatters` attempts to be an easily extendable formatting module which can generate rich text, links and HTML.
15
+
16
+ ## Installation
17
+
18
+ Add this line to your application's Gemfile:
19
+
20
+ gem 'xrb-formatters'
21
+
22
+ And then execute:
23
+
24
+ $ bundle
25
+
26
+ Or install it yourself as:
27
+
28
+ $ gem install xrb-formatters
29
+
30
+ ## Usage
31
+
32
+ The most basic usage involves converting model data into presentation text by
33
+ a mapping corresponding to the objects type:
34
+
35
+ ``` ruby
36
+ formatter = XRB::Formatters::Formatter.new
37
+
38
+ formatter.for(String) do |value, **options|
39
+ "String: #{value}"
40
+ end
41
+
42
+ expect(formatter.format("foobar")).to be == "String: foobar"
43
+ ```
44
+
45
+ For more examples please see `spec/`.
46
+
47
+ ## Contributing
48
+
49
+ We welcome contributions to this project.
50
+
51
+ 1. Fork it.
52
+ 2. Create your feature branch (`git checkout -b my-new-feature`).
53
+ 3. Commit your changes (`git commit -am 'Add some feature'`).
54
+ 4. Push to the branch (`git push origin my-new-feature`).
55
+ 5. Create new Pull Request.
56
+
57
+ ### Developer Certificate of Origin
58
+
59
+ This project uses the [Developer Certificate of Origin](https://developercertificate.org/). All contributors to this project must agree to this document to have their contributions accepted.
60
+
61
+ ### Contributor Covenant
62
+
63
+ This project is governed by the [Contributor Covenant](https://www.contributor-covenant.org/). All contributors and participants agree to abide by its terms.
data.tar.gz.sig ADDED
Binary file
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xrb-formatters
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Samuel Williams
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIE2DCCA0CgAwIBAgIBATANBgkqhkiG9w0BAQsFADBhMRgwFgYDVQQDDA9zYW11
14
+ ZWwud2lsbGlhbXMxHTAbBgoJkiaJk/IsZAEZFg1vcmlvbnRyYW5zZmVyMRIwEAYK
15
+ CZImiZPyLGQBGRYCY28xEjAQBgoJkiaJk/IsZAEZFgJuejAeFw0yMjA4MDYwNDUz
16
+ MjRaFw0zMjA4MDMwNDUzMjRaMGExGDAWBgNVBAMMD3NhbXVlbC53aWxsaWFtczEd
17
+ MBsGCgmSJomT8ixkARkWDW9yaW9udHJhbnNmZXIxEjAQBgoJkiaJk/IsZAEZFgJj
18
+ bzESMBAGCgmSJomT8ixkARkWAm56MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB
19
+ igKCAYEAomvSopQXQ24+9DBB6I6jxRI2auu3VVb4nOjmmHq7XWM4u3HL+pni63X2
20
+ 9qZdoq9xt7H+RPbwL28LDpDNflYQXoOhoVhQ37Pjn9YDjl8/4/9xa9+NUpl9XDIW
21
+ sGkaOY0eqsQm1pEWkHJr3zn/fxoKPZPfaJOglovdxf7dgsHz67Xgd/ka+Wo1YqoE
22
+ e5AUKRwUuvaUaumAKgPH+4E4oiLXI4T1Ff5Q7xxv6yXvHuYtlMHhYfgNn8iiW8WN
23
+ XibYXPNP7NtieSQqwR/xM6IRSoyXKuS+ZNGDPUUGk8RoiV/xvVN4LrVm9upSc0ss
24
+ RZ6qwOQmXCo/lLcDUxJAgG95cPw//sI00tZan75VgsGzSWAOdjQpFM0l4dxvKwHn
25
+ tUeT3ZsAgt0JnGqNm2Bkz81kG4A2hSyFZTFA8vZGhp+hz+8Q573tAR89y9YJBdYM
26
+ zp0FM4zwMNEUwgfRzv1tEVVUEXmoFCyhzonUUw4nE4CFu/sE3ffhjKcXcY//qiSW
27
+ xm4erY3XAgMBAAGjgZowgZcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O
28
+ BBYEFO9t7XWuFf2SKLmuijgqR4sGDlRsMC4GA1UdEQQnMCWBI3NhbXVlbC53aWxs
29
+ aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MC4GA1UdEgQnMCWBI3NhbXVlbC53aWxs
30
+ aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MA0GCSqGSIb3DQEBCwUAA4IBgQB5sxkE
31
+ cBsSYwK6fYpM+hA5B5yZY2+L0Z+27jF1pWGgbhPH8/FjjBLVn+VFok3CDpRqwXCl
32
+ xCO40JEkKdznNy2avOMra6PFiQyOE74kCtv7P+Fdc+FhgqI5lMon6tt9rNeXmnW/
33
+ c1NaMRdxy999hmRGzUSFjozcCwxpy/LwabxtdXwXgSay4mQ32EDjqR1TixS1+smp
34
+ 8C/NCWgpIfzpHGJsjvmH2wAfKtTTqB9CVKLCWEnCHyCaRVuKkrKjqhYCdmMBqCws
35
+ JkxfQWC+jBVeG9ZtPhQgZpfhvh+6hMhraUYRQ6XGyvBqEUe+yo6DKIT3MtGE2+CP
36
+ eX9i9ZWBydWb8/rvmwmX2kkcBbX0hZS1rcR593hGc61JR6lvkGYQ2MYskBveyaxt
37
+ Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
38
+ voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
39
+ -----END CERTIFICATE-----
40
+ date: 2024-05-08 00:00:00.000000000 Z
41
+ dependencies:
42
+ - !ruby/object:Gem::Dependency
43
+ name: mapping
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '1.1'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '1.1'
56
+ - !ruby/object:Gem::Dependency
57
+ name: xrb
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '0.6'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '0.6'
70
+ description:
71
+ email:
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - lib/xrb/formatters.rb
77
+ - lib/xrb/formatters/formatter.rb
78
+ - lib/xrb/formatters/html/accept_checkbox.rb
79
+ - lib/xrb/formatters/html/definition_list_form.rb
80
+ - lib/xrb/formatters/html/form_formatter.rb
81
+ - lib/xrb/formatters/html/label_form.rb
82
+ - lib/xrb/formatters/html/option_select.rb
83
+ - lib/xrb/formatters/html/radio_select.rb
84
+ - lib/xrb/formatters/markdown.rb
85
+ - lib/xrb/formatters/relative_time.rb
86
+ - lib/xrb/formatters/truncated_text.rb
87
+ - lib/xrb/formatters/version.rb
88
+ - license.md
89
+ - readme.md
90
+ homepage: https://github.com/ioquatix/xrb-formatters
91
+ licenses:
92
+ - MIT
93
+ metadata:
94
+ funding_uri: https://github.com/sponsors/ioquatix/
95
+ source_code_uri: https://github.com/ioquatix/xrb-formatters.git
96
+ post_install_message:
97
+ rdoc_options: []
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '3.1'
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubygems_version: 3.5.3
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: Formatters for XRB, to assist with typical views and form based interfaces.
115
+ test_files: []
metadata.gz.sig ADDED
Binary file