campo 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data.tar.gz.sig ADDED
Binary file
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ .DS_Store
2
+ *.swp
3
+
4
+ .rvmrc
5
+ *.tmproj
6
+ key
7
+ log/
8
+ files/
9
+ thin/
10
+ .sass-cache/
11
+ run/
12
+ app/.sass-cache/
13
+ scratch/
14
+ *.gem
15
+ coverage/
16
+ .yardoc/
17
+ docs/
data/CHANGES ADDED
@@ -0,0 +1,65 @@
1
+ v0.2.0 All Base fields can now take a block and the convenience methodsl, allowing them to nest other elements regardless of whether the parent is a form or a select. This basically makes it a lot easier to use a literal as the root of the document.
2
+
3
+ v0.1.1 Label naming a bit better... possibly! It breaks on _ and capitalises when trying for a default.
4
+
5
+ v0.1.0 Added support for helper methods in amongst haml attributes. More convenience methods like `radio`. Support for nesting of fieldsets. `.select` takes a block too.
6
+
7
+ v0.0.30 Changed main output to allow a whole form to be wrapped in a literal, for instance when wanting to wrap it in a div. Fragments now require 'false' to be passed along with the tag class. No change for whole forms.
8
+
9
+ v0.0.29 Added more tests and a couple of convenience methods - bit_of_ruby and literal.
10
+
11
+ v0.0.28 Somehow a typo that caused a bug got committed. Fixed it.
12
+
13
+ v0.0.27 changes to allow fragments to be output without the variable declarations at the top, and for all children to output too.
14
+
15
+ v0.0.26 Bugfix
16
+
17
+ v0.0.25 Added checkbox convenience method.
18
+
19
+ v0.0.24 Fixed inners for textarea, properly evaluated now, and slight change to submit button so it's not passed on submit as a parameter.
20
+
21
+ v0.0.23 Inners are capitalised and attributes downcased, to make using convenience methods more convenient.
22
+
23
+ v0.0.22 Bugfixes, bit of internal rejigging, and fieldset convenience method now takes a block, it's more natural.
24
+
25
+ v0.0.21 Added convenience method for Textarea.
26
+
27
+ v0.0.20 Bugfixes to Textarea and some other bits of assignment code, as it just didn't work.
28
+
29
+ v0.0.19 Added convenience method for submit button.
30
+
31
+ v0.0.18 Fix to errors caused by attributes that aren't strings; labelling can be added by .method or by push.
32
+
33
+ v0.0.17 Bug fix/internal API change, forced by changes in 0.0.16
34
+
35
+ v0.0.16 can pass a ruby insert for Haml to a select tag
36
+
37
+ v0.0.15 Select#with_default can now be used with an and array and/or a block.
38
+
39
+ v0.0.14 Added some specs and made minor changes to code.
40
+
41
+ v0.0.13 Added convenience methods for adding text and select tags (with a default option tag), and problem with spaces in names/values for attribute methods.
42
+
43
+ v0.0.12 Tab index for fields is automatically generated.
44
+
45
+ v0.0.11 No need to explicitly give a label the name, it picks it up from the field.
46
+
47
+ v0.0.10 All options get id's and locals too.
48
+
49
+ v0.0.9 Added fieldsets and legends.
50
+
51
+ v0.0.8 Adds id's to input fields, making accessible labels.
52
+
53
+ v0.0.7 Select tags with dynamic option tags possible.
54
+
55
+ v0.0.6 Added in another local, called inners for dynamic inner stuff. Changed locals to atts as it was messing up sinatra/haml. Added in more defaults to locals to avoid errors.
56
+
57
+ v0.0.5 Added in locals, which makes the parts of the form (possibly) dynamic.
58
+
59
+ v0.0.4 Moved output delegates to blocks on path to adding locals easier.
60
+
61
+ v0.0.3 Simplified API, removed unnecessary classes
62
+
63
+ v0.0.2 Submit button and convenience method for output.
64
+
65
+ v0.0.1 Input text fields, textarea, with labels.
data/README.markdown ADDED
@@ -0,0 +1,276 @@
1
+ # Campo #
2
+
3
+ A static dynamic form builder into haml. Yep, static _and_ dynamic. Use it to statically create a form into haml, but you may notice it's taken advantage of haml's "add a hash to the front of the attributes and it'll get merged" property. http://haml-lang.com/docs/yardoc/file.HAML\_REFERENCE.html#attribute_methods. More on that below.
4
+
5
+ Btw, I'll be using this with Sinatra, if you're using Rails you'll need to work out how that's done as I don't know.
6
+
7
+ ## Why though? ##
8
+
9
+ However nice Haml is, it's still a lot of effort to build a form. If you've got lots of forms it's worse. The long term plan is to link this in to Sequel.
10
+
11
+ ## Example! ##
12
+
13
+ Here's an example form:
14
+
15
+ # This bit is to simulate the output I'd usually get from calling a database model for a lookup table
16
+ genders = [["1", "Male"], ["2", "Female"]]
17
+
18
+ # Now starts the real action #
19
+
20
+ form = Campo.form "myform", action: "/my/form/update/"
21
+
22
+ form.fieldset "Your details" do |f|
23
+ f.text "full_name", size: 60
24
+ f.text "dob", "Date of birth: ", size: 8
25
+
26
+ f.select( "gender_id", {opts: genders }).with_default.labelled( "Gender: " )
27
+
28
+ f.select "teas" do |s|
29
+ s.with_default
30
+ s.option("ceylon")
31
+ s.option("breakfast")
32
+ s.option("earl grey")
33
+ s.option("oolong")
34
+ s.option("sencha")
35
+ end.labelled "Favourite tea:"
36
+
37
+ f.text "occupation", "Occupation: ", size: 60
38
+ f.text "phone_landline", "Phone landline : ", size: 20
39
+ f.text "phone_mobile", "Phone mobile : ", size: 20
40
+
41
+ f.submit "Save"
42
+
43
+ end
44
+
45
+ and the output:
46
+
47
+
48
+ puts Campo.output( form )
49
+
50
+ - atts = {} if atts.nil?
51
+ - atts.default = {} if atts.default.nil?
52
+ - inners.default = "" if inners.default.nil?
53
+ - i = 0 # for tabindex
54
+
55
+ %form{ atts[:myform], method: "POST", action: "/my/form/update/", name: "myform", }
56
+ %fieldset{ }
57
+ %legend{ }Your details
58
+ %label{ for: "full_name", }
59
+ Full name:
60
+ %input{ atts[:full_name], tabindex: "#{i += 1}", type: "text", id: "full_name", size: "60", name: "full_name", }
61
+ %label{ for: "dob", }
62
+ Date of birth:
63
+ %input{ atts[:dob], tabindex: "#{i += 1}", type: "text", id: "dob", size: "8", name: "dob", }
64
+ %label{ for: "gender_id", }
65
+ Gender:
66
+ %select{ atts[:gender_id], tabindex: "#{i += 1}", name: "gender_id", }
67
+ %option{ value: "", disabled: "disabled", name: "gender_id", }Choose one:
68
+ %option{ atts[:gender_id_1], value: "1", id: "gender_id_1", name: "gender_id", }Male
69
+ %option{ atts[:gender_id_2], value: "2", id: "gender_id_2", name: "gender_id", }Female
70
+ %label{ for: "teas", }
71
+ Favourite tea:
72
+ %select{ atts[:teas], tabindex: "#{i += 1}", name: "teas", }
73
+ %option{ value: "", disabled: "disabled", name: "teas", }Choose one:
74
+ %option{ atts[:teas_ceylon], value: "ceylon", id: "teas_ceylon", name: "teas", }Ceylon
75
+ %option{ atts[:teas_breakfast], value: "breakfast", id: "teas_breakfast", name: "teas", }Breakfast
76
+ %option{ atts[:teas_earl_grey], value: "earl grey", id: "teas_earl_grey", name: "teas", }Earl grey
77
+ %option{ atts[:teas_oolong], value: "oolong", id: "teas_oolong", name: "teas", }Oolong
78
+ %option{ atts[:teas_sencha], value: "sencha", id: "teas_sencha", name: "teas", }Sencha
79
+ %label{ for: "occupation", }
80
+ Occupation:
81
+ %input{ atts[:occupation], tabindex: "#{i += 1}", type: "text", id: "occupation", size: "60", name: "occupation", }
82
+ %label{ for: "phone_landline", }
83
+ Phone (landline):
84
+ %input{ atts[:phone_landline], tabindex: "#{i += 1}", type: "text", id: "phone_landline", size: "20", name: "phone_landline", }
85
+ %label{ for: "phone_mobile", }
86
+ Phone (mobile):
87
+ %input{ atts[:phone_mobile], tabindex: "#{i += 1}", type: "text", id: "phone_mobile", size: "20", name: "phone_mobile", }
88
+ %input{ atts[:Save_Save], tabindex: "#{i += 1}", type: "submit", id: "Save_Save", value: "Save", }
89
+
90
+ and that outputs:
91
+
92
+ puts Haml::Engine.new( Campo.output( form ) ).render
93
+
94
+ <form action='/my/form/update/' method='POST' name='myform'>
95
+ <fieldset>
96
+ <legend>Your details</legend>
97
+ <label for='full_name'>
98
+ Full name:
99
+ <input id='full_name' name='full_name' size='60' tabindex='1' type='text' />
100
+ </label>
101
+ Date of birth:
102
+ <input id='dob' name='dob' size='8' tabindex='2' type='text' />
103
+ </label>
104
+ <label for='gender_id'>
105
+ Gender:
106
+ <select name='gender_id' tabindex='3'>
107
+ <option disabled='disabled' name='gender_id' value=''>Choose one:</option>
108
+ <option id='gender_id_1' name='gender_id' value='1'>Male</option>
109
+ <option id='gender_id_2' name='gender_id' value='2'>Female</option>
110
+ </select>
111
+ </label>
112
+ <label for='teas'>
113
+ Favourite tea:
114
+ <select name='teas' tabindex='4'>
115
+ <option disabled='disabled' name='teas' value=''>Choose one:</option>
116
+ <option id='teas_ceylon' name='teas' value='ceylon'>Ceylon</option>
117
+ <option id='teas_breakfast' name='teas' value='breakfast'>Breakfast</option>
118
+ <option id='teas_earl_grey' name='teas' value='earl grey'>Earl grey</option>
119
+ <option id='teas_oolong' name='teas' value='oolong'>Oolong</option>
120
+ <option id='teas_sencha' name='teas' value='sencha'>Sencha</option>
121
+ </select>
122
+ </label>
123
+ <label for='occupation'>
124
+ Occupation:
125
+ <input id='occupation' name='occupation' size='60' tabindex='5' type='text' />
126
+ </label>
127
+ <label for='phone_landline'>
128
+ Phone (landline):
129
+ <input id='phone_landline' name='phone_landline' size='20' tabindex='6' type='text' />
130
+ </label>
131
+ <label for='phone_mobile'>
132
+ Phone (mobile):
133
+ <input id='phone_mobile' name='phone_mobile' size='20' tabindex='7' type='text' />
134
+ </label>
135
+ <input id='Save_Save' tabindex='8' type='submit' value='Save' />
136
+ </fieldset>
137
+ </form>
138
+
139
+
140
+ Back to the dynamic attributes mentioned earlier. What does this mean? You can pass in a local to dynamically alter the form based on server side logic.
141
+
142
+ These get added to the top, to provide sane defaults:
143
+
144
+ - atts = {} if atts.nil?
145
+ - atts.default = {} if atts.default.nil?
146
+ - inners = {} if inners.nil?
147
+ - inners.default = "" if inners.default.nil?
148
+
149
+ In the select tag (below), notice how each tag gets a local variable added to the front. You can either fill that variable with a hash pair, or an empty hash gets passed and nothing happens.
150
+
151
+ Here's the Campo code:
152
+
153
+ form = Campo.form "best_bands", action: "/best/bands/" do |form|
154
+ form.select("bands").option("Suede").option("Blur").option("Oasis").option("Echobelly").option("Pulp").option("Supergrass").with_default.labelled("Favourite band:")
155
+ end
156
+
157
+ or
158
+
159
+ form = Campo.form "best_bands", action: "/best/bands/"
160
+ form.select("bands") do |s|
161
+ s.with_default
162
+ s.option("Suede")
163
+ s.option("Blur")
164
+ s.option("Oasis")
165
+ s.option("Echobelly")
166
+ s.option("Pulp")
167
+ s.option("Supergrass")
168
+ end.labelled("Favourite band:")
169
+
170
+ (or mix and match blocks, .new, arrays and hashes)
171
+
172
+ Campo.output form # generate the haml
173
+
174
+
175
+ And the Haml generated:
176
+
177
+ %form{ atts[:best_bands], method: "POST", action: "/best/bands/", name: "best_bands", }
178
+ %label{ for: "bands", }
179
+ Favourite band:
180
+ %select{ atts[:bands], tabindex: "#{i += 1}", name: "bands", }
181
+ %option{ value: "", disabled: "disabled", name: "bands", }Choose one:
182
+ %option{ atts[:bands_suede], value: "Suede", id: "bands_suede", name: "bands", }Suede
183
+ %option{ atts[:bands_blur], value: "Blur", id: "bands_blur", name: "bands", }Blur
184
+ %option{ atts[:bands_oasis], value: "Oasis", id: "bands_oasis", name: "bands", }Oasis
185
+ %option{ atts[:bands_echobelly], value: "Echobelly", id: "bands_echobelly", name: "bands", }Echobelly
186
+ %option{ atts[:bands_pulp], value: "Pulp", id: "bands_pulp", name: "bands", }Pulp
187
+ %option{ atts[:bands_supergrass], value: "Supergrass", id: "bands_supergrass", name: "bands", }Supergrass
188
+
189
+
190
+ If you wanted to select "Blur" dynamically (and you should, but I'd accept Suede) you might do:
191
+
192
+ atts[:bands_blur] = {selected: "selected"}
193
+
194
+ and pass it in to the form when the view is rendered, and the tag would change from:
195
+
196
+ <option id='bands_blur' name='bands' value='Blur'>Blur</option>
197
+
198
+ to:
199
+
200
+ <option selected='selected' id='bands_blur' name='bands' value='Blur'>Blur</option>
201
+
202
+ You can do this with any kind of attribute you wish to add. For example:
203
+
204
+
205
+ atts[:bands_blur] = {not_worth_listening_to: "selected"}
206
+
207
+ If you want to use helpers in the attributes, like sinatra's `uri` helper, then add a quote to the front:
208
+
209
+ form = Campo::Form.new "best_bands", action: %Q!"uri("/best/bands/")!
210
+
211
+ outputs:
212
+
213
+ %form{ atts[:best_bands], method: "POST", action: uri("/best/bands/"), name: "best_bands", }
214
+
215
+ If the helper isn't among the attributes, add a "=" to the front as you would in the haml:
216
+
217
+ form.bit_of_ruby( "= 5 + 1" ) }
218
+
219
+ outputs:
220
+
221
+ %form{ atts[:best_bands], method: "POST", action: uri("/best/bands/"), name: "best_bands", }
222
+ = 5 + 1
223
+
224
+ It's really just a literal:
225
+
226
+
227
+ form = Campo.form "favourite_teas", action: %Q!"uri("/fav/teas/")! do |form|
228
+ form.select("teas").with_default.option("ceylon").option("breakfast").option("earl grey").labelled("Favourite tea:")
229
+ form.literal %Q<%p= "I like tea!">
230
+ end
231
+ Campo.output form
232
+
233
+
234
+ %form{ atts[:favourite_teas], method: "POST", action: uri("/fav/teas/"), name: "favourite_teas", }
235
+ %label{ for: "teas", }
236
+ Favourite tea:
237
+ %select{ atts[:teas], tabindex: "#{i += 1}", name: "teas", }
238
+ %option{ value: "", disabled: "disabled", name: "teas", }Choose one:
239
+ %option{ atts[:teas_ceylon], value: "ceylon", id: "teas_ceylon", name: "teas", }Ceylon
240
+ %option{ atts[:teas_breakfast], value: "breakfast", id: "teas_breakfast", name: "teas", }Breakfast
241
+ %option{ atts[:teas_earl_grey], value: "earl grey", id: "teas_earl_grey", name: "teas", }Earl grey
242
+ %p= "I like tea!"
243
+
244
+ puts Haml::Engine.new( Campo.output form ).render
245
+
246
+ <form action='/fav/teas/' method='POST' name='favourite_teas'>
247
+ <label for='teas'>
248
+ Favourite tea:
249
+ <select name='teas' tabindex='1'>
250
+ <option disabled='disabled' name='teas' value=''>Choose one:</option>
251
+ <option id='teas_ceylon' name='teas' value='ceylon'>Ceylon</option>
252
+ <option id='teas_breakfast' name='teas' value='breakfast'>Breakfast</option>
253
+ <option id='teas_earl_grey' name='teas' value='earl grey'>Earl grey</option>
254
+ </select>
255
+ </label>
256
+ <p>I like tea!</p>
257
+ </form>
258
+
259
+ You can use literals to wrap forms in divs too:
260
+
261
+ doc = Campo.literal ".centred.form" do |wrapper|
262
+ wrapper << form # the form defined already above
263
+ end
264
+
265
+ puts Campo.output doc
266
+
267
+ .centred.form
268
+ %form{ atts[:favourite_teas], method: "POST", action: uri("/fav/teas/"), name: "favourite_teas", }
269
+ %label{ for: "teas", }
270
+ Favourite tea:
271
+ %select{ atts[:teas], tabindex: "#{i += 1}", name: "teas", }
272
+ %option{ value: "", disabled: "disabled", name: "teas", }Choose one:
273
+ %option{ atts[:teas_ceylon], value: "ceylon", id: "teas_ceylon", name: "teas", }Ceylon
274
+ %option{ atts[:teas_breakfast], value: "breakfast", id: "teas_breakfast", name: "teas", }Breakfast
275
+ %option{ atts[:teas_earl_grey], value: "earl grey", id: "teas_earl_grey", name: "teas", }Earl grey
276
+ %p= "I like tea!"
data/campo.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('./lib')
3
+ $:.unshift lib unless $:.include?(lib)
4
+ require './lib/campo/version.rb'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "campo"
8
+ s.summary = "Form builder for Haml"
9
+ s.description = <<-EOF
10
+ Form builder for Haml
11
+ EOF
12
+ s.version = Campo::VERSION
13
+ s.platform = Gem::Platform::RUBY
14
+ s.require_path = "lib"
15
+ s.required_ruby_version = ">= 1.9.2"
16
+ s.authors = ["Iain Barnett"]
17
+ s.files = `git ls-files`.split("\n")
18
+ s.add_dependency("haml", "~> 3.1.1")
19
+ s.email = ["iainspeed @nospam@ gmail.com"]
20
+ s.test_files = `git ls-files -- {test,spec,features}`.split("\n")
21
+ s.signing_key = ENV['HOME'] + '/.ssh/gem-private_key.pem'
22
+ s.cert_chain = [ENV['HOME'] + '/.ssh/gem-public_cert.pem']
23
+ end
data/lib/campo.rb ADDED
@@ -0,0 +1,507 @@
1
+ # encoding: UTF-8
2
+
3
+ module Campo
4
+ module Childish
5
+ def push=( child )
6
+ @fields << child
7
+ child.parent = self
8
+ self
9
+ end
10
+
11
+ alias :<< :push=
12
+
13
+ attr_accessor :parent
14
+ end # Childish
15
+
16
+ module Iding
17
+ def id_tag( val )
18
+ val.nil? ? "" : "_#{val}"
19
+ end
20
+ end # Iding
21
+
22
+ module Convenience
23
+
24
+
25
+ # @param [optional, Hash] attributes Any attributes you wish to add to the haml element.
26
+ # @example Fieldset as a block is easiest to read
27
+ # form.fieldset("Your details") do |f|
28
+ # f.text( "full_name", size: 60 )
29
+ # f.text( "dob", "Date of birth: ", size: 8 )
30
+ # end
31
+ def fieldset( text, attributes={}, &block )
32
+ fieldset = (Fieldset.new(attributes) << Legend.new( text ))
33
+ block.call( fieldset ) if block
34
+ self << fieldset
35
+ fieldset
36
+ end
37
+
38
+ # @example Add a bit of code to the markup
39
+ # form.bit_of_ruby( "= 5 + 1" ) }
40
+ def bit_of_ruby( *args )
41
+ tag = Campo::Haml_Ruby_Insert.new( *args )
42
+ self << tag
43
+ tag
44
+ end
45
+
46
+ alias :haml_ruby_insert :bit_of_ruby
47
+
48
+ # @example Output a literal string
49
+ # form.literal %Q!%p= "This is a paragraph "!
50
+ def literal( *args )
51
+ tag = Campo::Literal.new( *args )
52
+ self << tag
53
+ tag
54
+ end
55
+
56
+ # @example
57
+ # # Select with a block of options
58
+ # f.select("teas") do |s|
59
+ # s.with_default
60
+ # s.option("ceylon")
61
+ # s.option("breakfast")
62
+ # s.option("earl grey")
63
+ # s.option("oolong")
64
+ # s.option("sencha")
65
+ # end.labelled("Favourite tea:")
66
+ #
67
+ # # Select using chain of options
68
+ # form.select("bands").option("Suede").option("Blur").option("Oasis").option("Echobelly").option("Pulp").option("Supergrass").with_default.labelled("Favourite band:")
69
+ #
70
+ # @see Select
71
+ def select( *args, &block )
72
+ select = Campo::Select.new( *args, &block )
73
+ self << select
74
+ select
75
+ end
76
+
77
+ # Add an input with type of text
78
+ # @param [String] name The name html attribute.
79
+ # @param [optional, String, nil] label Give the label a name. Defaults to a capitalised name with _ replaced by spaces.
80
+ # @param [optional, Hash] attributes Any attributes you wish to add to the haml element.
81
+ # @example
82
+ # f.text "full_name", size: 60
83
+ # f.text "dob", "Date of birth: ", size: 8
84
+ # @return [Input]
85
+ # With the attribute `type=text`
86
+ def text( name, label=nil, attributes={} )
87
+ input( name, :text, label, attributes )
88
+ end
89
+
90
+ # @param (see #text)
91
+ def radio( name, label=nil, attributes={} )
92
+ input( name, :radio, label, attributes )
93
+ end
94
+
95
+ # @param (see #text)
96
+ def checkbox( name, label=nil, attributes={} )
97
+ input( name, :checkbox, label, attributes )
98
+ end
99
+
100
+
101
+ # @param (see #text)
102
+ # @param [:symbol] type The type html attribute.
103
+ def input( name, type, label=nil, attributes={} )
104
+ if label.kind_of? Hash
105
+ attributes = label
106
+ label = nil
107
+ end
108
+
109
+ field = Campo::Input.new( name, type, attributes ).labelled( label )
110
+ self << field
111
+ field
112
+ end
113
+
114
+ # @param [optional,String] name
115
+ # @param [optional, Hash] attributes Any attributes you wish to add to the haml element.
116
+ def submit( name="Submit", label_inner=nil, attributes={} )
117
+ submit = Campo::Input.new( name, :submit, {value: name}.merge(attributes) )
118
+ self << submit
119
+ submit
120
+ end
121
+
122
+
123
+ def textarea( *args )
124
+ textarea = Campo::Textarea.new( *args )
125
+ self << textarea
126
+ textarea
127
+ end
128
+ end # Convenience
129
+
130
+ module Helpers
131
+ # [ [id, lookup, selected || false], ... ]
132
+ def self.options_builder( name, opts )
133
+ return [] if opts.nil? || opts.empty?
134
+
135
+ opts.map do |opt|
136
+ id, lookup, selected, atts = opt
137
+ selected = selected ? true : false
138
+ atts = atts.nil? ? { } : atts
139
+
140
+ Campo::Option.new( name, id, lookup, selected, atts )
141
+ end
142
+ end
143
+
144
+ def self.options_outputter( opts=[] )
145
+ return "" if opts.nil? || opts.empty?
146
+ opts.map{|o| "#{o.output}\n" }.reduce(:+)
147
+ end
148
+ end # Helpers
149
+
150
+ @atts = {}
151
+
152
+ class << self
153
+ attr_accessor :atts
154
+ end
155
+
156
+ class Base
157
+ include Childish
158
+ include Iding
159
+ include Convenience
160
+
161
+ DEFAULT = { tabindex: nil }
162
+
163
+ attr_accessor :attributes, :fields
164
+
165
+ def initialize( name, attributes={}, &block )
166
+ @attributes = DEFAULT.merge( attributes.merge({name: name}) ).reject{|k,v| v.nil? }
167
+ @fields = []
168
+ block.call( self ) if block
169
+ self
170
+ end
171
+
172
+ def on_output( &block )
173
+ @output_listener = block
174
+ end
175
+
176
+ def output( n=0, tab=2 )
177
+ @output_listener.call n, tab
178
+ end
179
+
180
+ def labelled( inner=nil )
181
+ inner ||= self.attributes[:name].gsub("_"," ").capitalize
182
+ parent = self.parent
183
+ label = Label.new( %Q!#{@attributes[:name] + id_tag(@attributes[:value]).gsub(/\W/, "_")}!, inner ) << self
184
+ retval = if parent.nil?
185
+ label
186
+ else
187
+ parent.fields.delete self
188
+ parent << label
189
+ label
190
+ end
191
+
192
+ retval
193
+ end # labelled
194
+
195
+ def self.unhash( hash, skip=nil )
196
+ hash.reject{|k,v| v.nil? }.reject{|k,v| k.to_sym == skip.to_sym unless skip.nil? }.reduce(""){|mem, (k,v)| mem + %Q!#{k}: #{Base.quotable(v)}, !}
197
+ end
198
+
199
+ # if the string provided begins with a double quote but does not end in one, make it an unquoted string on output
200
+ # else, wrap it in quotes
201
+ def self.quotable( s )
202
+ retval = if s.respond_to?(:start_with?) && s.start_with?( %Q!"! ) &! s.end_with?( %Q!"! )
203
+ s[1.. -1] # chop the first character
204
+ else
205
+ %Q!"#{s}"! # wrap
206
+ end
207
+ end
208
+
209
+
210
+ def self.output( top, so_far="", count=0, tab=2 )
211
+ so_far << "#{top.output( count, tab )}\n"
212
+ count += 1
213
+ if top.respond_to?( :fields ) && top.fields.length >= 1
214
+ top.fields.each do |field|
215
+ so_far = Base.output( field, so_far, count, tab )
216
+ end
217
+ end
218
+
219
+ so_far
220
+ end
221
+
222
+ end # Base
223
+
224
+ # @see Convenience#literal
225
+ def self.literal( *args, &block )
226
+ Campo::Literal.new( *args, &block )
227
+ end
228
+
229
+ # Pass anything but the form for the first argument to *not* have the local variable defaults added to the top
230
+ # @example
231
+ # Campo.output form # would add the default locals
232
+ # # these won't
233
+ # Campo.output :partial, input_field
234
+ # Campo.output false, label
235
+ # Campo.output true, fieldset
236
+ def self.output( *args )
237
+ s = <<STR
238
+ - atts = {} if atts.nil?
239
+ - atts.default = {} if atts.default.nil?
240
+ - inners = {} if inners.nil?
241
+ - inners.default = "" if inners.default.nil?
242
+ - i = 0 # for tabindex
243
+
244
+ STR
245
+
246
+
247
+ # default to true
248
+ whole_form = if args.first.kind_of? Campo::Base
249
+ true
250
+ else
251
+ args.shift
252
+ false
253
+ end
254
+
255
+ output = Base.output( *args )
256
+ output = s + output if whole_form
257
+ output
258
+ end # self.output
259
+
260
+ # end Campo methods
261
+
262
+
263
+
264
+ class Form < Base
265
+ DEFAULT = { method: "POST" }
266
+
267
+ # @param [String] name The form's name (html) attribute.
268
+ # @param [optional, Hash] attributes Html attributes. They can be anything you like. Defaults follow:
269
+ # @option attributes [String] :method ("POST")
270
+ # @example
271
+ # form = Campo::Form.new "example", "/path/to/post/to/" do |form|
272
+ # form.text "first_field"
273
+ # #... more fields follow
274
+ # end
275
+ def initialize(name, attributes={} )
276
+ super( name, DEFAULT.merge( attributes ) )
277
+ self.on_output do |n=0, tab=2|
278
+ %Q!#{" " * n * tab}%form{ atts[:#{name.gsub(/\W/, "_").downcase}], #{Base.unhash( @attributes )} }!
279
+ end
280
+
281
+ self
282
+ end
283
+
284
+
285
+ end # Form
286
+
287
+ # Generally, the first method you'll call.
288
+ # @example
289
+ # # Form with a block
290
+ # form = Campo.form "form1", action: "/go/for/it/" do |f|
291
+ # f.text "Hello"
292
+ # #... more fields follow
293
+ # end
294
+ #
295
+ # @param [String] name The form's name (html) attribute.
296
+ # @param [optional, Hash] attributes Html attributes. They can be anything you like. Defaults follow:
297
+ # @option attributes [String] :method ("POST") The method attribute for the form.
298
+ # @see Form#initialize
299
+ def self.form( name, attributes={}, &block )
300
+ Form.new( name, attributes, &block )
301
+ end
302
+
303
+
304
+ class Haml_Ruby_Insert < Base
305
+ def initialize( s )
306
+ super( nil ) # no name needed
307
+ @s = s.start_with?( '=' ) ? s : "= #{s}"
308
+
309
+ self.on_output do |n=0, tab=2|
310
+ (" " * n * tab) + @s
311
+ end
312
+ end
313
+ end # Haml_Ruby_Insert
314
+
315
+
316
+ # add whatever you need to with a literal
317
+ class Literal < Base
318
+ def initialize( s )
319
+ super( nil ) # no name needed
320
+ @s = s
321
+
322
+ self.on_output do |n=0, tab=2|
323
+ (" " * n * tab) + @s
324
+ end
325
+ self
326
+ end
327
+ end # Literal
328
+
329
+ class Select < Base
330
+ def initialize( name, params={} )
331
+ opts = params[:opts] || []
332
+ attributes = params[:attributes] || {}
333
+ haml_insert = params[:haml_insert] || nil
334
+
335
+ super( name, { tabindex: %q!#{i += 1}! }.merge(attributes) )
336
+
337
+ self.on_output do |n=0, tab=2|
338
+ %Q!#{" " * n * tab}%select{ atts[:#{name.gsub(/\W/, "_").downcase}], #{Base.unhash( @attributes )} }!
339
+ end
340
+
341
+ self.fields += Helpers.options_builder( name, opts ) unless opts.nil? || opts.empty?
342
+
343
+ self.fields << Haml_Ruby_Insert.new( haml_insert ) unless haml_insert.nil?
344
+
345
+
346
+ self
347
+ end # initialize
348
+
349
+ # @example (see Convenience#select)
350
+ def option( *args )
351
+ value = args.shift
352
+ inner = args.shift
353
+ selected, attributes = *args
354
+ inner = value.capitalize if inner.nil?
355
+ self << Campo::Option.new( @attributes[:name], value, inner, selected, attributes )
356
+ self
357
+ end
358
+
359
+
360
+ # @example
361
+ # As a default:
362
+ # form.select("teas").with_default.option("ceylon")
363
+ # # output:
364
+ # %select{ atts[:teas], tabindex: "#{i += 1}", name: "teas", }
365
+ # %option{ value: "", disabled: "disabled", name: "teas", }Choose one:
366
+ # %option{ atts[:teas_ceylon], value: "ceylon", id: "teas_ceylon", name: "teas", }Ceylon
367
+ #
368
+ # form.select("teas").with_default("My fave tea is:").option("ceylon")
369
+ # # output:
370
+ # %select{ atts[:teas], tabindex: "#{i += 1}", name: "teas", }
371
+ # %option{ value: "", disabled: "disabled", name: "teas", }My fave tea is:
372
+ # %option{ atts[:teas_ceylon], value: "ceylon", id: "teas_ceylon", name: "teas", }Ceylon
373
+ def with_default( inner="Choose one:" )
374
+ self.fields.unshift Campo::Option.new( @attributes[:name], "", inner , nil, {disabled: "disabled" } )
375
+ self
376
+ end
377
+
378
+ # def mark_as_selected( val )
379
+ # fields.find {|field| field.value == val }.selected = {selected: "selected"}
380
+ # end
381
+ end # Select
382
+
383
+
384
+ class Option < Base
385
+ attr_accessor :value, :checked
386
+
387
+ def initialize( name, value, inner=nil, selected=nil, attributes={} )
388
+ attributes ||= {}
389
+ if inner.kind_of? TrueClass
390
+ selected = attributes
391
+ inner = nil
392
+ end
393
+
394
+ @inner = inner || value
395
+
396
+ if selected.kind_of? Hash
397
+ attributes = selected
398
+ selected = {}
399
+ end
400
+
401
+ attributes = { id: "#{(name.gsub(/\W/, "_") + id_tag(value).gsub(/\W/, "_")).downcase}" }.merge(attributes) unless value.nil? || value.to_s.empty?
402
+
403
+ super( name, {
404
+ value: value,
405
+ selected: (selected ? "selected" : nil)
406
+ }.merge( attributes ) )
407
+
408
+ atts_string = "atts[:#{@attributes[:id]}]," unless @attributes[:id].nil?
409
+
410
+ self.on_output do |n=0, tab=2|
411
+ %Q!#{" " * n * tab}%option{ #{atts_string} #{Base.unhash( @attributes )} }#{@inner}!
412
+ end
413
+
414
+ end #initialize
415
+ end # Option
416
+
417
+
418
+ # form << Campo::Input.new( "submit", :submit )
419
+ class Input < Base
420
+
421
+ #{ type: nil, value: nil, name: nil }
422
+ #{ size: nil, maxlength: nil, type: "text" }
423
+ #{ size: nil, maxlength: nil, type: "hidden" }
424
+ #{ type: "submit" }
425
+ def initialize( name, type=:text, attributes={} )
426
+ super( name,
427
+ { type: type.to_s,
428
+ id: "#{name}#{id_tag(attributes[:value]).gsub(/\W/, "_")}",
429
+ tabindex: %q!#{i += 1}!,
430
+ }.merge( attributes ) )
431
+
432
+
433
+ @attributes.delete(:name) if type == :submit
434
+
435
+ self.on_output do |n=0, tab=2|
436
+ %Q!#{" " * n * tab}%input{ atts[:#{name.gsub(/\W/, "_")}#{id_tag(attributes[:value]).gsub(/\W/, "_")}], #{Base.unhash( @attributes )} }!
437
+ end
438
+ end
439
+ end
440
+
441
+ class Fieldset < Base
442
+
443
+ def initialize( attributes={} )
444
+ super( nil, attributes )
445
+ @attributes.delete(:name)
446
+
447
+ self.on_output do |n=0, tab=2|
448
+ %Q!#{" " * n * tab}%fieldset{ #{Base.unhash( @attributes )} }!
449
+ end
450
+ end # initialize
451
+ end # Fieldset
452
+
453
+
454
+ class Legend < Base
455
+
456
+ def initialize( inner, attributes={} )
457
+ super( nil, attributes )
458
+ @attributes.delete(:name)
459
+ @inner = inner
460
+
461
+ self.on_output do |n=0, tab=2|
462
+ %Q!#{" " * n * tab}%legend{ #{Base.unhash( @attributes )} }#{@inner}!
463
+ end
464
+ end # initialize
465
+ end # Fieldset
466
+
467
+
468
+ class Label < Base
469
+
470
+ DEFAULT = { for: nil }
471
+
472
+ attr_reader :attributes, :fields
473
+
474
+ def initialize( name, inner=nil, attributes={} )
475
+ if inner.kind_of? Hash
476
+ attributes = inner
477
+ inner = nil
478
+ end
479
+ super( name, attributes )
480
+
481
+ @inner = inner
482
+
483
+ self.on_output do |n=0, tab=2|
484
+ %Q!#{" " * n * tab}%label{ for: "#{@attributes[:name]}", #{Base.unhash( @attributes, :name )} }\n#{" " * (n + 1) * tab}#{@inner}!
485
+ end
486
+ end
487
+
488
+ end # Label
489
+
490
+
491
+ class Textarea < Base
492
+ DEFAULT = { cols: 40, rows: 10, tabindex: %q!#{i += 1}! }
493
+
494
+ def initialize( name, inner=nil, attributes={} )
495
+ if inner.kind_of? Hash
496
+ attributes = inner
497
+ inner = nil
498
+ end
499
+ super( name, DEFAULT.merge( attributes ) )
500
+ @inner = inner
501
+ self.on_output do |n=0, tab=2|
502
+ %Q!#{" " * n * tab}%textarea{ atts[:#{name.gsub(/\W/, "_")}], #{Base.unhash( @attributes )} }= inners[:#{name.gsub(/\W/, "_")}] !
503
+ end
504
+ end
505
+ end # Textarea
506
+
507
+ end