demeler 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +0 -0
- data/CHANGELOG.md +2 -0
- data/LICENSE.md +9 -0
- data/README.md +383 -0
- data/demeler.gemspec +15 -0
- data/lib/demeler/version.rb +4 -0
- data/lib/demeler.rb +401 -0
- data/notes +13 -0
- data/spec/demeler.rb +336 -0
- metadata +55 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 60dd5907473caf7a0f8963ed8a5ef41c7c56ba7a
|
4
|
+
data.tar.gz: 9ef767a5cbf325bf6456b2a08d1b9a668e0317b7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 885c065028088be8c1d2545c3cd31d755b9a9cf6a1dc747a376cf8c1502bd12647ba12a59abf14b07016689446c37f3da10473c153387c919d0fea824dd01ee6
|
7
|
+
data.tar.gz: 4409d084b43ec05c2ccd3060023840a237695ed0038acfedc00c7f91088d60f3366a441f19437526bda1926edbdb7f30b35bbc67acc0901d9a4b4cf9ad17b7f2
|
data/.gitignore
ADDED
File without changes
|
data/CHANGELOG.md
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# The MIT License (MIT)
|
2
|
+
|
3
|
+
## Copyright ©2017 Michael J. Welch, Ph.D.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copyof this software and associated documentation files (the "Software"), to dealin the Software without restriction, including without limitation the rightsto use, copy, modify, merge, publish, distribute, sublicense, and/or sellcopies of the Software, and to permit persons to whom the Software isfurnished to do so, subject to the following conditions:
|
6
|
+
|
7
|
+
###The above copyright notice and this permission notice shall be included in allcopies or substantial portions of the Software.
|
8
|
+
|
9
|
+
###THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ORIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THEAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHERLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THESOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,383 @@
|
|
1
|
+
# Demeler Gem
|
2
|
+
|
3
|
+
### Copyright (c) 2017 Michael J Welch, Ph.D. mjwelchphd@gmail.com
|
4
|
+
All files in this distribution are subject to the terms of the MIT license.
|
5
|
+
|
6
|
+
This gem builds HTML code on-the-fly. The advantages are: (1) HTML code is properly formed with respect to tags and nesting; and (2) the code is dynamic, i.e., values from an object containing data (if used) are automatically extracted and inserted into the resultant HTML code, and (3) if there are errors, the error message is generated also.
|
7
|
+
|
8
|
+
The French word démêler means "to unravel," and that's sort of what this gem does. Démêler is pronounced "day-meh-lay." It unravels your inputs to form HTML code. The diacritical marks are not used in the name for compatibility.
|
9
|
+
|
10
|
+
This class doesn't depend on any particular framework, but I use it with Ruby Sequel.
|
11
|
+
|
12
|
+
|
13
|
+
## The Demeler gem generates HTML from three inputs:
|
14
|
+
* A Ruby source file you write;
|
15
|
+
* A Hash-based object you provide, like Sequel::Model objects
|
16
|
+
* An errors list inside the Hash-based object
|
17
|
+
|
18
|
+
Let's start with the most basic form, a simple example. Run `irb` and enter this:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
require 'demeler'
|
22
|
+
html = Demeler.build(nil,true) do
|
23
|
+
html do
|
24
|
+
head do
|
25
|
+
title "Hello, World!"
|
26
|
+
end
|
27
|
+
body do
|
28
|
+
h1 "Hello, World!"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
puts html
|
33
|
+
```
|
34
|
+
|
35
|
+
You'll get the html code like this:
|
36
|
+
|
37
|
+
```html
|
38
|
+
<!-- begin generated output -->
|
39
|
+
<html>
|
40
|
+
<head>
|
41
|
+
<title>Hello, World!</title>
|
42
|
+
</head>
|
43
|
+
<body>
|
44
|
+
<h1>Hello, World!</h1>
|
45
|
+
</body>
|
46
|
+
</html>
|
47
|
+
<!-- end generated output -->
|
48
|
+
```
|
49
|
+
|
50
|
+
### Why bother with Demeler? Why not just write HTML?
|
51
|
+
|
52
|
+
There are several reasons to use this gem:
|
53
|
+
|
54
|
+
* You write in Ruby code
|
55
|
+
* Demeler balances out all the HTML tags
|
56
|
+
* Demeler optionally formats the HTML, producing human readable output
|
57
|
+
* Demeler can receive an object with data (such as a Sequel::Model object), and automatically insert the values
|
58
|
+
* Demeler can insert error messages your controller inserts into the object
|
59
|
+
|
60
|
+
## You can also use the gem directly
|
61
|
+
|
62
|
+
You can instantiate a Demeler object, and call its methods:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
require 'demeler'
|
66
|
+
d = Demeler.new
|
67
|
+
d.html do
|
68
|
+
d.head do
|
69
|
+
d.title("Hello, World!")
|
70
|
+
end
|
71
|
+
d.body do
|
72
|
+
d.h1("Hello, World!")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
puts d.to_html
|
76
|
+
```
|
77
|
+
|
78
|
+
## Fields from an object can be inserted automatically
|
79
|
+
|
80
|
+
You can automatically load the values from a Sequel::Model object, or you can define an object and use it in place of Sequel. To define an object, use a definition similar to this:
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
class Obj<Hash
|
84
|
+
attr_accessor :errors
|
85
|
+
def initialize
|
86
|
+
@errors = {}
|
87
|
+
end
|
88
|
+
end
|
89
|
+
```
|
90
|
+
|
91
|
+
Your new object is just a Hash+, so you can assign it values like this:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
obj = Obj.new
|
95
|
+
obj[:username] = "michael"
|
96
|
+
obj[:password] = "my-password"
|
97
|
+
```
|
98
|
+
|
99
|
+
The object can now be used to fill `input` fields in a form:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
html = Demeler.build(obj,true) do
|
103
|
+
text :username
|
104
|
+
password :password
|
105
|
+
end
|
106
|
+
puts html
|
107
|
+
```
|
108
|
+
|
109
|
+
That code will automatically insert the values from `obj` for you.
|
110
|
+
|
111
|
+
> _NOTE: When the first argument is a symbol, it is used as the name of the field._
|
112
|
+
|
113
|
+
```html
|
114
|
+
<!-- begin generated output -->
|
115
|
+
<input name="username" type="text" value="michael" />
|
116
|
+
<input name="password" type="password" value="my-password" />
|
117
|
+
<!-- end generated output -->
|
118
|
+
```
|
119
|
+
|
120
|
+
### Demeler creates error messages, too
|
121
|
+
|
122
|
+
You can put an error message into the object you created if your validation finds something wrong. Just insert a Hash element with the name of the element, and an array of lines, thusly:
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
obj = Obj.new
|
126
|
+
obj[:username] = "michael"
|
127
|
+
obj[:password] = "my-password"
|
128
|
+
obj.errors[:username] = ["This username is already taken"]
|
129
|
+
|
130
|
+
html = Demeler.build(obj,true) do
|
131
|
+
text :username
|
132
|
+
password :password
|
133
|
+
end
|
134
|
+
puts html
|
135
|
+
```
|
136
|
+
|
137
|
+
This will generate the HTML with the added message, as well:
|
138
|
+
|
139
|
+
```html
|
140
|
+
<!-- begin generated output -->
|
141
|
+
<input name="username" type="text" value="michael" /><warn> <-- This username is already taken</warn>
|
142
|
+
<input name="password" type="password" value="my-password" />
|
143
|
+
<!-- end generated output -->
|
144
|
+
```
|
145
|
+
|
146
|
+
Notice that the error is surrounded by `<warn>...</warn>` tags. You can define how you want those to format the message using CSS. For example, if you just want the message to be displayed in red, use:
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
style "warn {color: red;}"
|
150
|
+
```
|
151
|
+
|
152
|
+
in your Demeler code like this:
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
html = Demeler.build(obj,true) do
|
156
|
+
style "warn {color: red;}"
|
157
|
+
text :username
|
158
|
+
password :password
|
159
|
+
end
|
160
|
+
puts html
|
161
|
+
```
|
162
|
+
|
163
|
+
## Adding attributes to your tags
|
164
|
+
|
165
|
+
You can add attributes by just adding them to the end of the tag. For example, if I want the username input tag to display in with a blue background, I can use:
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
html = Demeler.build(obj,true) do
|
169
|
+
style ".blue {color: blue;}"
|
170
|
+
text :username, :class=>"blue"
|
171
|
+
password :password
|
172
|
+
end
|
173
|
+
puts html
|
174
|
+
```
|
175
|
+
|
176
|
+
The HTML code generated will be:
|
177
|
+
|
178
|
+
```html
|
179
|
+
<!-- begin generated output -->
|
180
|
+
<style>.blue {color: blue;}</style>
|
181
|
+
<input name="username" class="blue" type="text" value="michael" /><warn> <-- This username is already taken</warn>
|
182
|
+
<input name="password" type="password" value="my-password" />
|
183
|
+
<!-- end generated output -->
|
184
|
+
```
|
185
|
+
|
186
|
+
Any attribute can be added in this way.
|
187
|
+
|
188
|
+
## Embedding text between tags on one line
|
189
|
+
|
190
|
+
Normally, anything in brackets {} is embedded like this: `p{"Some text."}` yields:
|
191
|
+
|
192
|
+
```html
|
193
|
+
<!-- begin generated output -->
|
194
|
+
<p>
|
195
|
+
Some text.
|
196
|
+
</p>
|
197
|
+
<!-- end generated output -->
|
198
|
+
```
|
199
|
+
|
200
|
+
You can make it come out on one line by using the :text attribute: `p :text=>"Some text."` yields:
|
201
|
+
|
202
|
+
```html
|
203
|
+
<!-- begin generated output -->
|
204
|
+
<p>Some text.</p>
|
205
|
+
<!-- end generated output -->
|
206
|
+
```
|
207
|
+
|
208
|
+
In most cases, this can be achieved just by eliminating the {}: `p "Some text." yields:
|
209
|
+
|
210
|
+
```html
|
211
|
+
<!-- begin generated output -->
|
212
|
+
<p>Some text.</p>
|
213
|
+
<!-- end generated output -->
|
214
|
+
```
|
215
|
+
|
216
|
+
This is because the solo string is converted to a :text argument automatically.
|
217
|
+
|
218
|
+
|
219
|
+
## Demeler Interface Definitions
|
220
|
+
|
221
|
+
The Demeler gem accepts any random tag you want to give it, but non-input tags and input tags are treated differently. Input tags are :button, :checkbox, :color, :date, :datetime_local, :email, :hidden, :image, :month, :number, :password, :range, :radio, :reset, :search, :select, :submit, :tel, :text, :time, :url, and :week. All other tags are non-input tags.
|
222
|
+
|
223
|
+
Input tags are treated differently because they have special characteristics, like automatically setting from a form object passed to Demeler. Therefore, we'll start with non-input tags first.
|
224
|
+
|
225
|
+
Non-input tags can accept a variety of parameter arrangements to allow for different situations. Each different situation is described below, and there are examples to help you understand.
|
226
|
+
|
227
|
+
### Non-Input Tag Parameter Formats
|
228
|
+
|
229
|
+
#### tag opts
|
230
|
+
|
231
|
+
_Opts is a Hash with attributes._
|
232
|
+
|
233
|
+
```ruby
|
234
|
+
puts (Demeler.build(nil, true) do
|
235
|
+
div :class=>"div-class" do
|
236
|
+
"..."
|
237
|
+
end
|
238
|
+
end)
|
239
|
+
|
240
|
+
<!-- begin generated output -->
|
241
|
+
<div class="div-class">
|
242
|
+
...
|
243
|
+
</div>
|
244
|
+
<!-- end generated output -->
|
245
|
+
```
|
246
|
+
#### tag
|
247
|
+
|
248
|
+
_A solo tag._
|
249
|
+
|
250
|
+
```ruby
|
251
|
+
puts (Demeler.build(nil, true) do
|
252
|
+
br
|
253
|
+
end)
|
254
|
+
|
255
|
+
<!-- begin generated output -->
|
256
|
+
<br />
|
257
|
+
<!-- end generated output -->
|
258
|
+
```
|
259
|
+
|
260
|
+
#### tag string
|
261
|
+
|
262
|
+
_A solo string._
|
263
|
+
|
264
|
+
```ruby
|
265
|
+
puts (Demeler.build(nil, true) do
|
266
|
+
p "This is a paragraph."
|
267
|
+
end)
|
268
|
+
|
269
|
+
<!-- begin generated output -->
|
270
|
+
<p>This is a paragraph.</p>
|
271
|
+
<!-- end generated output -->
|
272
|
+
```
|
273
|
+
|
274
|
+
_Or for longer text, you can use the block format._
|
275
|
+
|
276
|
+
```ruby
|
277
|
+
puts (Demeler.build(nil, true) do
|
278
|
+
p { "Un débris de hameau où quatre maisons fleuries d'orchis émergent des blés drus et hauts. Ce sont les Bastides Blanches, à mi-chemin entre la plaine et le grand désert lavandier, à l'ombre des monts de Lure. C'est là que vivent douze personnes, deux ménages, plus Gagou l'innocent." }
|
279
|
+
end)
|
280
|
+
|
281
|
+
<!-- begin generated output -->
|
282
|
+
<p>
|
283
|
+
Un débris de hameau où quatre maisons fleuries d'orchis émergent des blés drus et hauts. Ce sont les Bastides Blanches, à mi-chemin entre la plaine et le grand désert lavandier, à l'ombre des monts de Lure. C'est là que vivent douze personnes, deux ménages, plus Gagou l'innocent.
|
284
|
+
</p>
|
285
|
+
```
|
286
|
+
|
287
|
+
#### tag symbol
|
288
|
+
|
289
|
+
_The symbol is used as the name of the tag._ This option is used with input tags (see below).
|
290
|
+
|
291
|
+
#### tag symbol, hash
|
292
|
+
|
293
|
+
_This option generates_
|
294
|
+
|
295
|
+
|
296
|
+
#### tag symbol, string
|
297
|
+
|
298
|
+
_This option generates a tag with the name **symbol**, and the string between beginning and ending tags. If the tag is a :label, and the `label for` matches an `input name`, if the `input` has no `id`, one will be added.
|
299
|
+
|
300
|
+
```ruby
|
301
|
+
puts (Demeler.build(nil, true) do
|
302
|
+
label :username, "Enter Username"
|
303
|
+
text :username
|
304
|
+
end)
|
305
|
+
|
306
|
+
<!-- begin generated output -->
|
307
|
+
<label for="username">Enter Username</label>
|
308
|
+
<input name="username" type="text" id="username" />
|
309
|
+
<!-- end generated output -->
|
310
|
+
```
|
311
|
+
|
312
|
+
_Tags other than `label` will have a `name` attribute instead of a `for` attribute._
|
313
|
+
|
314
|
+
## A Bigger Example of a Demeler Script
|
315
|
+
|
316
|
+
### Generate a login screen.
|
317
|
+
|
318
|
+
This example will be expanded in the future.
|
319
|
+
|
320
|
+
The Ruby code:
|
321
|
+
|
322
|
+
```ruby
|
323
|
+
html = Demeler.build(nil,true) do
|
324
|
+
h1("Please Login")
|
325
|
+
form(:method=>:post, :action=>"/authenticate") do
|
326
|
+
table do
|
327
|
+
tr do
|
328
|
+
td { label(:username, "User Name") }
|
329
|
+
td { text(:username, :size=>30) }
|
330
|
+
end
|
331
|
+
|
332
|
+
tr do
|
333
|
+
td { label(:password, "Password") }
|
334
|
+
td { password(:password, :size=>30) }
|
335
|
+
end
|
336
|
+
|
337
|
+
tr do
|
338
|
+
td {}
|
339
|
+
td { submit("Log In") }
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
p { alink("Forgot password?", :href=>"/forgot") }
|
344
|
+
end
|
345
|
+
```
|
346
|
+
|
347
|
+
The generated HTML:
|
348
|
+
|
349
|
+
```html
|
350
|
+
<!-- begin generated output -->
|
351
|
+
<h1>Please Login</h1>
|
352
|
+
<form method="post" action="/authenticate">
|
353
|
+
<table>
|
354
|
+
<tr>
|
355
|
+
<td>
|
356
|
+
<label for="username">User Name</label>
|
357
|
+
</td>
|
358
|
+
<td>
|
359
|
+
<input name="username" size="30" type="text" id="username" />
|
360
|
+
</td>
|
361
|
+
</tr>
|
362
|
+
<tr>
|
363
|
+
<td>
|
364
|
+
<label for="password">Password</label>
|
365
|
+
</td>
|
366
|
+
<td>
|
367
|
+
<input name="password" size="30" type="password" id="password" />
|
368
|
+
</td>
|
369
|
+
</tr>
|
370
|
+
<tr>
|
371
|
+
<td>
|
372
|
+
</td>
|
373
|
+
<td>
|
374
|
+
<input type="submit" value="Log In" />
|
375
|
+
</td>
|
376
|
+
</tr>
|
377
|
+
</table>
|
378
|
+
</form>
|
379
|
+
<p>
|
380
|
+
<a href="/forgot">Forgot password?</a>
|
381
|
+
</p>
|
382
|
+
<!-- end generated output -->
|
383
|
+
```
|
data/demeler.gemspec
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative File.dirname(__FILE__) + '/lib/demeler/version'
|
2
|
+
include Version
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.author = "Michael J. Welch, Ph.D."
|
5
|
+
s.files = Dir.glob(["CHANGELOG.md", "LICENSE.md", "README.md", "demeler.gemspec", "lib/*", "lib/demeler/*", "notes", "spec/*", ".gitignore"])
|
6
|
+
s.name = 'demeler'
|
7
|
+
s.require_paths = ["lib"]
|
8
|
+
s.summary = 'Yet another HTML generator.'
|
9
|
+
s.version = VERSION
|
10
|
+
s.date = MODIFIED
|
11
|
+
s.email = 'mjwelchphd@gmail.com'
|
12
|
+
s.homepage = 'http://rubygems.org/gems/demeler'
|
13
|
+
s.license = 'MIT'
|
14
|
+
s.description = "This gem takes your ruby input, plus an object such as a Sequel::Model object, and generates HTML code. If the object has values, they're inserted into the HTML, and if the object has error messages, code is generated to display them. You can use CSS, but it's not automated in this class of methods."
|
15
|
+
end
|
data/lib/demeler.rb
ADDED
@@ -0,0 +1,401 @@
|
|
1
|
+
# Copyright (c) 2009 Michael Fellinger m.fellinger@gmail.com
|
2
|
+
# Copyright (c) 2017 Michael J Welch, Ph.D. mjwelchphd@gmail.com
|
3
|
+
# All files in this distribution are subject to the terms of the MIT license.
|
4
|
+
#
|
5
|
+
# This work was based on Fellinger's Gestalt and Blueform, but is essentially
|
6
|
+
# a completely new version which is not a drop-in replacement for either.
|
7
|
+
# There's probably less than a dozen lines of Fellinger's original code left here.
|
8
|
+
# Nevertheless, the basic concept is Fellinger's, and it's a good one.
|
9
|
+
#
|
10
|
+
# This gem builds HTML code on-the-fly. The advantages are: (1) HTML code is
|
11
|
+
# properly formed with respect to tags and nesting; and (2) the code is dynamic,
|
12
|
+
# i.e., values from an object containing data (if used) are automatically
|
13
|
+
# extracted and inserted into the resultant HTML code, and if there are errors,
|
14
|
+
# the error message is generated also.
|
15
|
+
#
|
16
|
+
# The French word démêler means "to unravel," and that's sort of what this gem
|
17
|
+
# does. It unravels your inputs to form HTML code. The diacritical marks are
|
18
|
+
# not used in the name for compatibility.
|
19
|
+
#
|
20
|
+
# This class doesn't depend on any particular framework, but I use it with
|
21
|
+
# Ruby Sequel.
|
22
|
+
|
23
|
+
class Demeler
|
24
|
+
attr_reader :out, :obj
|
25
|
+
|
26
|
+
# These calls are effectively generated in the same way as 'text' input
|
27
|
+
# tags. Method_missing just does a substitution to implement them.
|
28
|
+
MethodsLikeInputText = [:button, :color, :date, :datetime_local, :email, \
|
29
|
+
:hidden, :image, :month, :number, :password, :range, :reset, :search, \
|
30
|
+
:submit, :tel, :text, :time, :url, :week]
|
31
|
+
|
32
|
+
##
|
33
|
+
# The default way to start building your markup.
|
34
|
+
# Takes a block and returns the markup.
|
35
|
+
#
|
36
|
+
# @param [Proc] block
|
37
|
+
#
|
38
|
+
def self.build(obj=nil, gen_html=false, &block)
|
39
|
+
demeler = self.new(obj, &block)
|
40
|
+
if gen_html then demeler.to_html else demeler.to_s end
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# Demeler.new builds HTML from Ruby code.
|
45
|
+
# You can either access #out, .to_s or .to_html to
|
46
|
+
# return the actual markup.
|
47
|
+
#
|
48
|
+
# A note of warning: you'll get extra spaces in textareas if you use .to_html.
|
49
|
+
#
|
50
|
+
# @param [object] obj--a Sequel::Model object, or Hash object with an added 'errors' field.
|
51
|
+
#
|
52
|
+
# To use this without Sequel, you can use an object like this:
|
53
|
+
# class Obj<Hash
|
54
|
+
# attr_accessor :errors
|
55
|
+
# def initialize
|
56
|
+
# @errors = {}
|
57
|
+
# end
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
def initialize(obj=nil, &block)
|
61
|
+
raise ArgumentError.new("The object passed to Demeler must have an errors field containing a Hash") if obj && !defined?(obj.errors)
|
62
|
+
@obj = obj
|
63
|
+
clear
|
64
|
+
instance_eval(&block) if block_given?
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Clear out the data in order to start over with the same Demeler obj
|
69
|
+
#
|
70
|
+
def clear
|
71
|
+
@level = 0
|
72
|
+
@out = []
|
73
|
+
@labels = []
|
74
|
+
end
|
75
|
+
|
76
|
+
##
|
77
|
+
# Catch tag calls that have not been pre-defined.
|
78
|
+
#
|
79
|
+
# @param [String] meth The method that was called.
|
80
|
+
# @param [Hash] args Additional arguments passed to the called method.
|
81
|
+
# @param [Proc] block.
|
82
|
+
#
|
83
|
+
def method_missing(meth, *args, &block)
|
84
|
+
# This code allows for some input tags that work like <input type="text" ...> to
|
85
|
+
# work--for example g.password works in place of g.input(:type=>:password, ...)
|
86
|
+
if MethodsLikeInputText.index(meth) # TODO!
|
87
|
+
args << {} if !args[-1].kind_of?(Hash)
|
88
|
+
args.last[:type] = meth
|
89
|
+
meth = :input
|
90
|
+
end
|
91
|
+
tag_generator(meth, args, &block)
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# Workaround for Kernel#p to make <p /> tags possible.
|
96
|
+
#
|
97
|
+
# @param [Hash] args Extra arguments that should be processed before
|
98
|
+
# creating the paragraph tag.
|
99
|
+
# @param [Proc] block
|
100
|
+
#
|
101
|
+
def p(*args, &block)
|
102
|
+
tag_generator(:p, args, &block)
|
103
|
+
end
|
104
|
+
|
105
|
+
##
|
106
|
+
# The #alink method simplyfies the generation of <a>...</a> tags
|
107
|
+
#
|
108
|
+
# @param [Array] args Extra arguments that should be processed before
|
109
|
+
# creating the 'a' tag.
|
110
|
+
# @param [Proc] block
|
111
|
+
#
|
112
|
+
def alink(text, args={})
|
113
|
+
raise ArgumentError.new("In Demeler#alink, expected String for argument 1, text") if !text.kind_of?(String)
|
114
|
+
raise ArgumentError.new("In Demeler#alink, expected Hash for argument 2, opts") if !args.kind_of?(Hash)
|
115
|
+
raise ArgumentError.new("In Demeler#alink, expected an href option in opts") if !args[:href]
|
116
|
+
opts = args.clone
|
117
|
+
opts[:text] = text
|
118
|
+
tag_generator(:a, opts)
|
119
|
+
end
|
120
|
+
|
121
|
+
##
|
122
|
+
# The checkbox shortcut
|
123
|
+
#
|
124
|
+
# @param [Symbol] name Base Name of the control (numbers 1..n will be added)
|
125
|
+
# @param [Hash] opts Attributes for the control
|
126
|
+
# @param [Hash] value=>nomenclature pairs
|
127
|
+
#
|
128
|
+
# @example
|
129
|
+
# g.checkbox(:vehicle, opts, :volvo=>"Volvo", :saab=>"Saab", :mercedes=>"Mercedes", :audi=>"Audi")
|
130
|
+
# @object [Sequel::Model]
|
131
|
+
#
|
132
|
+
#
|
133
|
+
# @note: first argument becomes the :name plus a number starting at 1, i.e., "vehicle1", etc.
|
134
|
+
#
|
135
|
+
def checkbox(name, opts, values)
|
136
|
+
raise ArgumentError.new("In Demeler#checkbox, expected Symbol for argument 1, name") if !name.kind_of?(Symbol)
|
137
|
+
raise ArgumentError.new("In Demeler#checkbox, expected Hash for argument 2, opts") if !opts.kind_of?(Hash)
|
138
|
+
raise ArgumentError.new("In Demeler#checkbox, expected Hash for argument 3, values") if !values.kind_of?(Hash)
|
139
|
+
n = 0
|
140
|
+
data = if @obj then @obj[name] else nil end
|
141
|
+
case data.class.name
|
142
|
+
when "String"
|
143
|
+
data = data.split(",")
|
144
|
+
when "Array"
|
145
|
+
# it's alreay in the form we want
|
146
|
+
when "Hash"
|
147
|
+
data = data.values
|
148
|
+
else
|
149
|
+
data = nil
|
150
|
+
end
|
151
|
+
values.each do |value,nomenclature|
|
152
|
+
sets = opts.clone
|
153
|
+
sets[:name] = "#{name}[#{n+=1}]".to_sym
|
154
|
+
sets[:type] = :checkbox
|
155
|
+
sets[:value] = value
|
156
|
+
sets[:text] = nomenclature
|
157
|
+
sets[:checked] = 'true' if data && data.index(value.to_s)
|
158
|
+
tag_generator(:input, sets)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
##
|
163
|
+
# The radio shortcut
|
164
|
+
#
|
165
|
+
# @param [Hash] args Attributes for the control
|
166
|
+
#
|
167
|
+
# @example
|
168
|
+
# g.radio(:name=>:vehicle, :volvo=>"Volvo", :saab=>"Saab", :mercedes=>"Mercedes", :audi=>"Audi")
|
169
|
+
#
|
170
|
+
# @note: first argument is the :name; without the name, the radio control won't work
|
171
|
+
#
|
172
|
+
def radio(name, opts, values)
|
173
|
+
raise ArgumentError.new("In Demeler#radio, expected Symbol for argument 1, name") if !name.kind_of?(Symbol)
|
174
|
+
raise ArgumentError.new("In Demeler#radio, expected Hash for argument 2, opts") if !opts.kind_of?(Hash)
|
175
|
+
raise ArgumentError.new("In Demeler#radio, expected Hash for argument 3, values") if !values.kind_of?(Hash)
|
176
|
+
data = if @obj then @obj[name] else nil end
|
177
|
+
values.each do |value,nomenclature|
|
178
|
+
sets = opts.clone
|
179
|
+
sets[:name] = "#{name}".to_sym
|
180
|
+
sets[:type] = :radio
|
181
|
+
sets[:value] = value
|
182
|
+
sets[:text] = nomenclature
|
183
|
+
sets[:checked] = 'true' if data==value.to_s
|
184
|
+
tag_generator(:input, sets)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
##
|
189
|
+
# The select (select_tag) shortcut
|
190
|
+
#
|
191
|
+
# @param [Symbol] name The name of the SELECT statement
|
192
|
+
# @param [Hash] opts Options for the SELECT statement
|
193
|
+
# @param [Hash] args Attributes (OPTIONS) for the SELECT
|
194
|
+
#
|
195
|
+
# @example
|
196
|
+
# g.select(:vehicle, {}, :volvo=>"Volvo", :saab=>"Saab", :mercedes=>"Mercedes", :audi=>"Audi")
|
197
|
+
#
|
198
|
+
# @note: first argument is the :name=>"vehicle"
|
199
|
+
# @note: the second argument is a Hash or nil
|
200
|
+
#
|
201
|
+
def select(name, opts, values)
|
202
|
+
raise ArgumentError.new("In Demeler#select, expected Symbol for argument 1, name") if !name.kind_of?(Symbol)
|
203
|
+
raise ArgumentError.new("In Demeler#select, expected Hash for argument 2, opts") if !opts.kind_of?(Hash)
|
204
|
+
raise ArgumentError.new("In Demeler#select, expected Hash for argument 3, values") if !values.kind_of?(Hash)
|
205
|
+
opts = {:name=>name}
|
206
|
+
opts.merge!(opts)
|
207
|
+
data = if @obj then @obj[name] else nil end
|
208
|
+
tag_generator(:select, [opts]) do
|
209
|
+
values.each do |value,nomenclature|
|
210
|
+
sets = {:value=>value}
|
211
|
+
sets[:selected] = 'true' if data==value.to_s
|
212
|
+
sets[:text] = nomenclature
|
213
|
+
tag_generator(:option, [sets])
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
##
|
219
|
+
# The submit shortcut
|
220
|
+
#
|
221
|
+
# @param [String] text The text which the button will display
|
222
|
+
# @param [Hash] opts Options for the SUBMIT statement
|
223
|
+
#
|
224
|
+
# @example
|
225
|
+
# g.submit("Accept Changes", {})
|
226
|
+
#
|
227
|
+
def submit(text, opts={})
|
228
|
+
attr = {:type=>:submit}
|
229
|
+
attr[:value] = text
|
230
|
+
attr.merge!(opts)
|
231
|
+
tag_generator(:input, attr)
|
232
|
+
end
|
233
|
+
|
234
|
+
##
|
235
|
+
# The tag_generator method
|
236
|
+
#
|
237
|
+
# @param [Symbol] meth The type of control to be generated
|
238
|
+
# @param [Hash] args Options for the tag being generated
|
239
|
+
# @param [Proc] block A block which will be called to get input or nest tags
|
240
|
+
#
|
241
|
+
# @note The :text option will insert text between the opening and closing tag;
|
242
|
+
# It's useful to create one-line tags with text inserted.
|
243
|
+
#
|
244
|
+
def tag_generator(meth, args=[], &block)
|
245
|
+
# this check catches a loop before it bombs the Demeler class
|
246
|
+
raise StandardError.new("looping on #{meth.inspect}, @out=#{@out.inspect}") if (@level += 1)>500
|
247
|
+
|
248
|
+
# This part examines the variations in possible inputs,
|
249
|
+
# and rearranges them to suit tag_generator
|
250
|
+
case
|
251
|
+
when args.kind_of?(Hash)
|
252
|
+
# args is a hash (attributes only)
|
253
|
+
attr = args
|
254
|
+
when args.size==0
|
255
|
+
# args is empty array
|
256
|
+
attr = {}
|
257
|
+
when args.size==1 && args[0].kind_of?(String)
|
258
|
+
# args is array of 1 string
|
259
|
+
attr = {:text=>args[0]}
|
260
|
+
when args.size==1 && args[0].kind_of?(Symbol)
|
261
|
+
# args is array of 1 symbol (used as 'name')
|
262
|
+
attr = {:name=>args[0]}
|
263
|
+
when args.size==1 && args[0].kind_of?(Hash)
|
264
|
+
# args is array of 1 hash (same as args is a hash)
|
265
|
+
attr = args[0]
|
266
|
+
when args.size==2 && args[0].kind_of?(Symbol) && args[1].kind_of?(Hash)
|
267
|
+
# args is an array of symbol ('name') and hash ('attributes')
|
268
|
+
# both name and attributes, i.e., g.div(:list, :class=>'list-class')
|
269
|
+
attr = {:name=>args[0]}.merge(args[1])
|
270
|
+
when args.size==2 && args[0].kind_of?(Symbol) && args[1].kind_of?(String)
|
271
|
+
# args is array of symbol ('name') and string ('text')
|
272
|
+
# both name and text, i.e., g.label(:list, "List")
|
273
|
+
case meth
|
274
|
+
when :label
|
275
|
+
attr = {:for=>args[0]}.merge({:text=>args[1]})
|
276
|
+
@labels << args[0]
|
277
|
+
else
|
278
|
+
attr = {:name=>args[0]}.merge({:text=>args[1]})
|
279
|
+
end
|
280
|
+
else
|
281
|
+
raise ArgumentError.new("Too many arguments in Demeler#tag_generator: meth=>#{meth.inspect}, args=>#{args.inspect}")
|
282
|
+
end
|
283
|
+
|
284
|
+
# This part extracts a value out of the form's object (if any)
|
285
|
+
name = attr[:name]
|
286
|
+
case
|
287
|
+
when name.nil?
|
288
|
+
when @obj.nil?
|
289
|
+
when !attr[:value].nil?
|
290
|
+
when @obj[name].nil?
|
291
|
+
when @obj[name].kind_of?(String) && @obj[name].empty?
|
292
|
+
when meth==:textarea
|
293
|
+
attr[:text] = @obj[name] if !attr.has_key?(:text)
|
294
|
+
else
|
295
|
+
attr[:value] = @obj[name] if !attr.has_key?(:value)
|
296
|
+
end
|
297
|
+
|
298
|
+
# If a label was previously defined for this :input,
|
299
|
+
# add an :id attribute automatically
|
300
|
+
attr[:id] = name if meth==:input && !attr.has_key?(:id) && @labels.index(name)
|
301
|
+
|
302
|
+
# This part extracts the text (if any)--the text
|
303
|
+
# is used in place of a block for tags like 'label'
|
304
|
+
text = attr.delete(:text)
|
305
|
+
case
|
306
|
+
when text.nil?
|
307
|
+
text = []
|
308
|
+
when text.kind_of?(String)
|
309
|
+
text = [text]
|
310
|
+
when text.kind_of?(Array)
|
311
|
+
else
|
312
|
+
raise ArgumentError.new("In Demeler#tag_generator, expected Array or String for text (value for textarea, or ")
|
313
|
+
end
|
314
|
+
|
315
|
+
# make sure there's at least one (empty) string for textarea because
|
316
|
+
# a textarea tag with no "block" makes the browser act wierd, even if it's
|
317
|
+
# self-closing, i.e., <textarea ... />
|
318
|
+
text = [""] if meth==:textarea && text.empty? && !block_given?
|
319
|
+
|
320
|
+
# In case there is an error message for this field,
|
321
|
+
# prepare the message now to add following the field
|
322
|
+
if @obj && (list = @obj.errors[name])
|
323
|
+
raise ArgumentError.new("The error message, if any, must be an array of Strings") if !list.kind_of?(Array)
|
324
|
+
error = if [:input, :select].index(meth) then list.first else nil end
|
325
|
+
message = if error then "<warn> <-- #{error}</warn>" else nil end
|
326
|
+
else
|
327
|
+
message = ""
|
328
|
+
end
|
329
|
+
|
330
|
+
# This is where the actual HTML is generated--it's structured this
|
331
|
+
# way to be sure that only WHOLE tags are placed into @out--it's
|
332
|
+
# done this way to facilitate #to_html
|
333
|
+
case
|
334
|
+
when !text.empty?
|
335
|
+
temp = text.join("\n")
|
336
|
+
@out << "<#{meth}#{attr.map{|k,v| %[ #{k}="#{v}"]}.join}>#{temp}</#{meth}>#{message}"
|
337
|
+
when block_given?
|
338
|
+
@out << "<#{meth}#{attr.map{|k,v| %[ #{k}="#{v}"]}.join}>"
|
339
|
+
temp = yield
|
340
|
+
@out << temp if temp && temp.kind_of?(String)
|
341
|
+
@out << "</#{meth}>#{message}"
|
342
|
+
else
|
343
|
+
@out << "<#{meth}#{attr.map{|k,v| %[ #{k}="#{v}"]}.join} />#{message}"
|
344
|
+
end
|
345
|
+
|
346
|
+
@level -= 1
|
347
|
+
nil
|
348
|
+
end
|
349
|
+
|
350
|
+
##
|
351
|
+
# Convert the final output of Demeler to a string.
|
352
|
+
# This method has the following alias: "to_str".
|
353
|
+
#
|
354
|
+
# @return [String]
|
355
|
+
#
|
356
|
+
def to_s
|
357
|
+
@out.join
|
358
|
+
end
|
359
|
+
|
360
|
+
##
|
361
|
+
# This method is part of #to_html below.
|
362
|
+
#
|
363
|
+
def write_html(indent,part)
|
364
|
+
# "<!-- #{indent} --> #{' '*(if indent<0 then 0 else indent end)}#{part}\n"
|
365
|
+
"#{' '*(if indent<0 then 0 else indent end)}#{part}\n"
|
366
|
+
end
|
367
|
+
|
368
|
+
##
|
369
|
+
# Method for converting the results of Demeler to a
|
370
|
+
# human readable string. This isn't recommended for
|
371
|
+
# production because it requires much more time to
|
372
|
+
# generate the HTML output than to_s.
|
373
|
+
#
|
374
|
+
# @return [String] The formatted form output
|
375
|
+
#
|
376
|
+
def to_html
|
377
|
+
# output the segments, but adjust the indentation
|
378
|
+
indent = 0
|
379
|
+
html = "<!-- begin generated output -->\n"
|
380
|
+
@out.each do |part|
|
381
|
+
case
|
382
|
+
when part =~ /^<\/.*>$/
|
383
|
+
indent -= 1
|
384
|
+
html << write_html(indent,part)
|
385
|
+
when part =~ /^<.*<\/.*>$/
|
386
|
+
html << write_html(indent,part)
|
387
|
+
when part =~ /^<.*\/>$/
|
388
|
+
html << write_html(indent,part)
|
389
|
+
when part =~ /^<.*>$/
|
390
|
+
html << write_html(indent,part)
|
391
|
+
indent += 1
|
392
|
+
else
|
393
|
+
html << write_html(indent,part)
|
394
|
+
end
|
395
|
+
end
|
396
|
+
# return the formatted string
|
397
|
+
html << "<!-- end generated output -->\n"
|
398
|
+
return html
|
399
|
+
end # to_html
|
400
|
+
|
401
|
+
end
|
data/notes
ADDED
data/spec/demeler.rb
ADDED
@@ -0,0 +1,336 @@
|
|
1
|
+
require "demeler"
|
2
|
+
require "bacon"
|
3
|
+
|
4
|
+
# bacon -Ilib spec/demeler.rb
|
5
|
+
|
6
|
+
class Obj<Hash
|
7
|
+
attr_accessor :errors
|
8
|
+
def initialize
|
9
|
+
@errors = {}
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "Simple Demeler with no Object" do
|
14
|
+
before do
|
15
|
+
@d = Demeler.new
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should be empty" do
|
19
|
+
@d.out.should.be.empty
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should return p" do
|
23
|
+
@d.clear
|
24
|
+
@d.p
|
25
|
+
@d.to_s.should.equal "<p />"
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should return p with embedded text" do
|
29
|
+
@d.clear
|
30
|
+
@d.p { "ABC" }
|
31
|
+
@d.to_s.should.equal "<p>ABC</p>"
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should return p with embedded HTML" do
|
35
|
+
@d.clear
|
36
|
+
@d.p { @d.br }
|
37
|
+
@d.to_s.should.equal "<p><br /></p>"
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should be a plain link" do
|
41
|
+
@d.clear
|
42
|
+
@d.alink("Registration", :href=>"registration")
|
43
|
+
@d.to_s.should.equal "<a href=\"registration\">Registration</a>"
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should be a plain checkbox control" do
|
47
|
+
@d.clear
|
48
|
+
@d.checkbox(:vehicle, {}, :volvo=>"Volvo", :saab=>"Saab", :mercedes=>"Mercedes", :audi=>"Audi")
|
49
|
+
@d.to_s.should.equal "<input name=\"vehicle[1]\" type=\"checkbox\" value=\"volvo\">Volvo</input><input name=\"vehicle[2]\" type=\"checkbox\" value=\"saab\">Saab</input><input name=\"vehicle[3]\" type=\"checkbox\" value=\"mercedes\">Mercedes</input><input name=\"vehicle[4]\" type=\"checkbox\" value=\"audi\">Audi</input>"
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should be a plain radio control" do
|
53
|
+
@d.clear
|
54
|
+
@d.radio(:vehicle, {}, :volvo=>"Volvo", :saab=>"Saab", :mercedes=>"Mercedes", :audi=>"Audi")
|
55
|
+
@d.to_s.should.equal "<input name=\"vehicle\" type=\"radio\" value=\"volvo\">Volvo</input><input name=\"vehicle\" type=\"radio\" value=\"saab\">Saab</input><input name=\"vehicle\" type=\"radio\" value=\"mercedes\">Mercedes</input><input name=\"vehicle\" type=\"radio\" value=\"audi\">Audi</input>"
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should be a plain select control" do
|
59
|
+
@d.clear
|
60
|
+
@d.select(:vehicle, {}, :volvo=>"Volvo", :saab=>"Saab", :mercedes=>"Mercedes", :audi=>"Audi")
|
61
|
+
@d.to_s.should.equal "<select name=\"vehicle\"><option value=\"volvo\">Volvo</option><option value=\"saab\">Saab</option><option value=\"mercedes\">Mercedes</option><option value=\"audi\">Audi</option></select>"
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should be a plain submit control" do
|
65
|
+
@d.clear
|
66
|
+
@d.submit("Go!")
|
67
|
+
@d.to_s.should.equal "<input type=\"submit\" value=\"Go!\" />"
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should add an :id if there is a matching label" do
|
71
|
+
@d.clear
|
72
|
+
@d.submit("Go!")
|
73
|
+
@d.label(:username, "Enter Username")
|
74
|
+
@d.text(:username)
|
75
|
+
@d.to_s.should.equal "<input type=\"submit\" value=\"Go!\" /><label for=\"username\">Enter Username</label><input name=\"username\" type=\"text\" id=\"username\" />"
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "Complex Demeler with Object" do
|
81
|
+
before do
|
82
|
+
obj = Obj.new
|
83
|
+
obj[:a_button] = "Push Me"
|
84
|
+
obj[:a_checkbox1] = {"2"=>"saab","4"=>"audi"}
|
85
|
+
obj[:a_checkbox2] = ["saab", "mercedes"]
|
86
|
+
obj[:a_checkbox3] = "volvo,mercedes"
|
87
|
+
obj[:a_color] = "#00FF00"
|
88
|
+
obj[:a_date1] = "2017-06-01"
|
89
|
+
obj[:a_date2] = "2017-07-02"
|
90
|
+
obj[:an_email] = "mike@czarmail.com"
|
91
|
+
obj[:a_hidden] = "hidden_data"
|
92
|
+
obj[:a_month] = "2017-06"
|
93
|
+
obj[:a_number] = "3"
|
94
|
+
obj[:a_password] = "fricken-password"
|
95
|
+
obj[:a_radio] = "saab"
|
96
|
+
obj[:a_range] = 2
|
97
|
+
obj[:a_search] = "search for this"
|
98
|
+
obj[:a_tel] = "951-929-2015"
|
99
|
+
obj[:a_text] = "This is just scat."
|
100
|
+
obj[:a_textarea1] = "Text for area 1."
|
101
|
+
obj[:a_textarea2] = "More text for area 2.\nThis text has multiple lines."
|
102
|
+
obj[:a_textarea4] = "Line 1"
|
103
|
+
obj[:a_time] = "23:24"
|
104
|
+
obj[:a_url] = "https://www.czarmail.com"
|
105
|
+
obj[:a_week] = "2018-W03"
|
106
|
+
|
107
|
+
@d = Demeler.new(obj)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "generate a style block" do
|
111
|
+
@d.clear
|
112
|
+
@d.style { "red { color: red; } warn { color: red; }" }
|
113
|
+
@d.to_s.should.equal "<style>red { color: red; } warn { color: red; }</style>"
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should be a button control with data" do
|
117
|
+
@d.clear
|
118
|
+
@d.button(:name=>:a_button)
|
119
|
+
@d.to_s.should.equal "<input name=\"a_button\" type=\"button\" value=\"Push Me\" />"
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should be a checkbox control with data" do
|
123
|
+
@d.clear
|
124
|
+
@d.checkbox(:a_checkbox1, {:class=>:check_class}, :volvo=>"Volvo", :saab=>"Saab", :mercedes=>"Mercedes", :audi=>"Audi")
|
125
|
+
@d.to_s.should.equal "<input class=\"check_class\" name=\"a_checkbox1[1]\" type=\"checkbox\" value=\"volvo\">Volvo</input><input class=\"check_class\" name=\"a_checkbox1[2]\" type=\"checkbox\" value=\"saab\" checked=\"true\">Saab</input><input class=\"check_class\" name=\"a_checkbox1[3]\" type=\"checkbox\" value=\"mercedes\">Mercedes</input><input class=\"check_class\" name=\"a_checkbox1[4]\" type=\"checkbox\" value=\"audi\" checked=\"true\">Audi</input>"
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should be a color control with data" do
|
129
|
+
@d.clear
|
130
|
+
@d.color(:a_color)
|
131
|
+
@d.to_s.should.equal "<input name=\"a_color\" type=\"color\" value=\"#00FF00\" />"
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should be a date control with data" do
|
135
|
+
@d.clear
|
136
|
+
@d.date(:a_date1)
|
137
|
+
@d.to_s.should.equal "<input name=\"a_date1\" type=\"date\" value=\"2017-06-01\" />"
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should be a date control with data and class" do
|
141
|
+
@d.clear
|
142
|
+
@d.date(:a_date2, :class=>:date_class)
|
143
|
+
@d.to_s.should.equal "<input name=\"a_date2\" class=\"date_class\" type=\"date\" value=\"2017-07-02\" />"
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should be a text control with data" do
|
147
|
+
@d.clear
|
148
|
+
@d.email(:an_email)
|
149
|
+
@d.to_s.should.equal "<input name=\"an_email\" type=\"email\" value=\"mike@czarmail.com\" />"
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should be a hidden field with data" do
|
153
|
+
@d.clear
|
154
|
+
@d.hidden(:a_hidden, :value=>3)
|
155
|
+
@d.to_s.should.equal "<input name=\"a_hidden\" value=\"3\" type=\"hidden\" />"
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should be an image button" do
|
159
|
+
@d.clear
|
160
|
+
@d.image(:src=>"images/czar-mail-logo.svg", :width=>80, :height=>30)
|
161
|
+
@d.to_s.should.equal "<input src=\"images/czar-mail-logo.svg\" width=\"80\" height=\"30\" type=\"image\" />"
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should be an image control" do
|
165
|
+
@d.clear
|
166
|
+
@d.img(:src=>"images/czar-mail-logo.svg", :width=>80, :height=>30)
|
167
|
+
@d.to_s.should.equal "<img src=\"images/czar-mail-logo.svg\" width=\"80\" height=\"30\" />"
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should be a month control with data" do
|
171
|
+
@d.clear
|
172
|
+
@d.month(:a_month)
|
173
|
+
@d.to_s.should.equal "<input name=\"a_month\" type=\"month\" value=\"2017-06\" />"
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should be a number control with data" do
|
177
|
+
@d.clear
|
178
|
+
@d.number(:a_number, :min=>1, :max=>5)
|
179
|
+
@d.to_s.should.equal "<input name=\"a_number\" min=\"1\" max=\"5\" type=\"number\" value=\"3\" />"
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should be a password control with data" do
|
183
|
+
@d.clear
|
184
|
+
@d.password(:a_password)
|
185
|
+
@d.to_s.should.equal "<input name=\"a_password\" type=\"password\" value=\"fricken-password\" />"
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should be a radio control with data" do
|
189
|
+
@d.clear
|
190
|
+
@d.radio(:a_radio, {:class=>:radio_class}, :volvo=>"Volvo", :saab=>"Saab", :mercedes=>"Mercedes", :audi=>"Audi")
|
191
|
+
@d.to_s.should.equal "<input class=\"radio_class\" name=\"a_radio\" type=\"radio\" value=\"volvo\">Volvo</input><input class=\"radio_class\" name=\"a_radio\" type=\"radio\" value=\"saab\" checked=\"true\">Saab</input><input class=\"radio_class\" name=\"a_radio\" type=\"radio\" value=\"mercedes\">Mercedes</input><input class=\"radio_class\" name=\"a_radio\" type=\"radio\" value=\"audi\">Audi</input>"
|
192
|
+
end
|
193
|
+
|
194
|
+
it "should be a range control with data" do
|
195
|
+
@d.clear
|
196
|
+
@d.range(:a_range, :min=>1, :max=>10)
|
197
|
+
@d.to_s.should.equal "<input name=\"a_range\" min=\"1\" max=\"10\" type=\"range\" value=\"2\" />"
|
198
|
+
end
|
199
|
+
|
200
|
+
it "should be a reset control" do
|
201
|
+
@d.clear
|
202
|
+
@d.reset
|
203
|
+
@d.to_s.should.equal "<input type=\"reset\" />"
|
204
|
+
end
|
205
|
+
|
206
|
+
it "should be a search control with data" do
|
207
|
+
@d.clear
|
208
|
+
@d.search(:a_search)
|
209
|
+
@d.to_s.should.equal "<input name=\"a_search\" type=\"search\" value=\"search for this\" />"
|
210
|
+
end
|
211
|
+
|
212
|
+
it "should be a submit control" do
|
213
|
+
@d.clear
|
214
|
+
@d.submit("Go!", :name=>:a_submit)
|
215
|
+
@d.to_s.should.equal "<input type=\"submit\" value=\"Go!\" name=\"a_submit\" />"
|
216
|
+
end
|
217
|
+
|
218
|
+
it "should be a tel (telephone) control with data" do
|
219
|
+
@d.clear
|
220
|
+
@d.tel(:a_tel)
|
221
|
+
@d.to_s.should.equal "<input name=\"a_tel\" type=\"tel\" value=\"951-929-2015\" />"
|
222
|
+
end
|
223
|
+
|
224
|
+
it "should be a text control with data" do
|
225
|
+
@d.clear
|
226
|
+
@d.text(:a_text, :size=>20)
|
227
|
+
@d.to_s.should.equal "<input name=\"a_text\" size=\"20\" type=\"text\" value=\"This is just scat.\" />"
|
228
|
+
end
|
229
|
+
|
230
|
+
it "should be a textarea (1) control with data" do
|
231
|
+
@d.clear
|
232
|
+
@d.textarea(:a_textarea1, :rows=>4, :cols=>50)
|
233
|
+
@d.to_s.should.equal "<textarea name=\"a_textarea1\" rows=\"4\" cols=\"50\">Text for area 1.</textarea>"
|
234
|
+
end
|
235
|
+
|
236
|
+
it "should be a textarea (2) control with data" do
|
237
|
+
@d.clear
|
238
|
+
@d.textarea(:a_textarea2, :rows=>4, :cols=>50) {'2'}
|
239
|
+
@d.to_s.should.equal "<textarea name=\"a_textarea2\" rows=\"4\" cols=\"50\">More text for area 2.\nThis text has multiple lines.</textarea>"
|
240
|
+
end
|
241
|
+
|
242
|
+
it "should be a textarea (3) control with NO data" do
|
243
|
+
@d.clear
|
244
|
+
@d.textarea(:a_textarea3, :rows=>4, :cols=>50, :text=>["Line 1","Line 2","Line 3"])
|
245
|
+
@d.to_s.should.equal "<textarea name=\"a_textarea3\" rows=\"4\" cols=\"50\">Line 1\nLine 2\nLine 3</textarea>"
|
246
|
+
end
|
247
|
+
|
248
|
+
it "should be a textarea (4) control with data" do
|
249
|
+
@d.clear
|
250
|
+
@d.textarea(:a_textarea4, :rows=>4, :cols=>50)
|
251
|
+
@d.to_s.should.equal "<textarea name=\"a_textarea4\" rows=\"4\" cols=\"50\">Line 1</textarea>"
|
252
|
+
end
|
253
|
+
|
254
|
+
it "should be a time control with data" do
|
255
|
+
@d.clear
|
256
|
+
@d.time(:a_time)
|
257
|
+
@d.to_s.should.equal "<input name=\"a_time\" type=\"time\" value=\"23:24\" />"
|
258
|
+
end
|
259
|
+
|
260
|
+
it "should be a url control with data" do
|
261
|
+
@d.clear
|
262
|
+
@d.url(:a_url)
|
263
|
+
@d.to_s.should.equal "<input name=\"a_url\" type=\"url\" value=\"https://www.czarmail.com\" />"
|
264
|
+
end
|
265
|
+
|
266
|
+
it "should be a week control with data" do
|
267
|
+
@d.clear
|
268
|
+
@d.week(:a_week)
|
269
|
+
@d.to_s.should.equal "<input name=\"a_week\" type=\"week\" value=\"2018-W03\" />"
|
270
|
+
end
|
271
|
+
|
272
|
+
it "should be a red tag with data" do
|
273
|
+
@d.clear
|
274
|
+
@d.red(:text=>"This ought to be red.")
|
275
|
+
@d.to_s.should.equal "<red>This ought to be red.</red>"
|
276
|
+
end
|
277
|
+
|
278
|
+
#=== Demeler Calls =====================================
|
279
|
+
|
280
|
+
it "should be demeler: args is a hash (attributes only)" do
|
281
|
+
@d.clear
|
282
|
+
@d.tag_generator(:div, {:class=>"div-class"}) { "..." }
|
283
|
+
@d.to_s.should.equal "<div class=\"div-class\">...</div>"
|
284
|
+
end
|
285
|
+
|
286
|
+
it "should be demeler: args is empty array" do
|
287
|
+
@d.clear
|
288
|
+
@d.tag_generator(:br, [])
|
289
|
+
@d.to_s.should.equal "<br />"
|
290
|
+
end
|
291
|
+
|
292
|
+
it "should be demeler: args is array of 1 string" do
|
293
|
+
@d.clear
|
294
|
+
@d.tag_generator(:textarea, ["Some text to edit."])
|
295
|
+
@d.to_s.should.equal "<textarea>Some text to edit.</textarea>"
|
296
|
+
end
|
297
|
+
|
298
|
+
it "should be demeler: args is array of 1 symbol (used as 'name')" do
|
299
|
+
@d.clear
|
300
|
+
@d.tag_generator(:div, [:div_name]) { "..." }
|
301
|
+
@d.to_s.should.equal "<div name=\"div_name\">...</div>"
|
302
|
+
end
|
303
|
+
|
304
|
+
it "should be demeler: args is array of 1 hash (same as args is a hash)" do
|
305
|
+
@d.clear
|
306
|
+
@d.tag_generator(:div, [{:class=>"div-name"}]) { "..." }
|
307
|
+
@d.to_s.should.equal "<div class=\"div-name\">...</div>"
|
308
|
+
end
|
309
|
+
|
310
|
+
it "should be demeler: args is an array of symbol ('name') and hash ('attributes')" do
|
311
|
+
@d.clear
|
312
|
+
@d.tag_generator(:div, [:list, :class=>'list-class']) { "..." }
|
313
|
+
@d.to_s.should.equal "<div name=\"list\" class=\"list-class\">...</div>"
|
314
|
+
end
|
315
|
+
|
316
|
+
it "should be demeler: args is array of symbol ('name') and string ('text' or 'for')" do
|
317
|
+
@d.clear
|
318
|
+
@d.tag_generator(:label, [:list, "List"])
|
319
|
+
@d.to_s.should.equal "<label for=\"list\">List</label>"
|
320
|
+
end
|
321
|
+
|
322
|
+
it "should have an error message" do
|
323
|
+
@d.clear
|
324
|
+
@d.obj.errors[:err] = ["Username already used."]
|
325
|
+
@d.tag_generator(:input, {:name=>:err, :value=>"bobama", :type=>:text})
|
326
|
+
@d.to_s.should.equal "<input name=\"err\" value=\"bobama\" type=\"text\" /><warn> <-- Username already used.</warn>"
|
327
|
+
end
|
328
|
+
|
329
|
+
it "should create user friendly HTML" do
|
330
|
+
@d.clear
|
331
|
+
@d.obj.errors[:err] = ["Username already used."]
|
332
|
+
@d.tag_generator(:input, {:name=>:err, :value=>"bobama", :type=>:text})
|
333
|
+
@d.to_html.should.equal "<!-- begin generated output -->\n<input name=\"err\" value=\"bobama\" type=\"text\" /><warn> <-- Username already used.</warn>\n<!-- end generated output -->\n"
|
334
|
+
end
|
335
|
+
|
336
|
+
end
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: demeler
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael J. Welch, Ph.D.
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-06-10 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: This gem takes your ruby input, plus an object such as a Sequel::Model
|
14
|
+
object, and generates HTML code. If the object has values, they're inserted into
|
15
|
+
the HTML, and if the object has error messages, code is generated to display them.
|
16
|
+
You can use CSS, but it's not automated in this class of methods.
|
17
|
+
email: mjwelchphd@gmail.com
|
18
|
+
executables: []
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- ".gitignore"
|
23
|
+
- CHANGELOG.md
|
24
|
+
- LICENSE.md
|
25
|
+
- README.md
|
26
|
+
- demeler.gemspec
|
27
|
+
- lib/demeler.rb
|
28
|
+
- lib/demeler/version.rb
|
29
|
+
- notes
|
30
|
+
- spec/demeler.rb
|
31
|
+
homepage: http://rubygems.org/gems/demeler
|
32
|
+
licenses:
|
33
|
+
- MIT
|
34
|
+
metadata: {}
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options: []
|
37
|
+
require_paths:
|
38
|
+
- lib
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
requirements: []
|
50
|
+
rubyforge_project:
|
51
|
+
rubygems_version: 2.5.1
|
52
|
+
signing_key:
|
53
|
+
specification_version: 4
|
54
|
+
summary: Yet another HTML generator.
|
55
|
+
test_files: []
|