xrb-formatters 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.
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