campo 0.3.4 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -15,3 +15,6 @@ scratch/
15
15
  coverage/
16
16
  .yardoc/
17
17
  docs/
18
+ vendor/
19
+ bin/
20
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format nested
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - ruby-head
6
+ bundler_args: --binstubs --path vendor
7
+ script: bundle exec rspec spec
@@ -1,3 +1,65 @@
1
+ ## v0.9.0 16th of June 2012 ##
2
+
3
+ * Updated the README with info on using plugins, and how to contribute.
4
+ * `describe` in the Aria plugin no longer needs a tuple to create a list, it can take an array of single valued arrays as the missing part of the tuple will default to an empty hash.
5
+ * Added testing for the develop branch to Travis.
6
+ * Moved most of the code to a separate file that is then required back in by lib/campo.rb, as specs were affected by the order they were run in. Now, each spec only `require` the files it needs and clear the plugins before running, which means they can be run in any order and not be affected.
7
+
8
+ ----
9
+
10
+ ## v0.8.3b 14th of May 2012 ##
11
+
12
+ * `with_default` can now have the 'disabled' attribute turned off.
13
+ * Added a lot of docs.
14
+
15
+ ----
16
+
17
+ v0.8.2b Literals can have attributes passed. Aria plugin's `describe` can take a list of messages and create an unordered list.
18
+
19
+ v0.8.1b Added a Rakefile with a task to generate the docs, for convenience. Fixed a couple of typos in the docs.
20
+
21
+ v0.8.0b The Aria plugin's `describe` now takes options for adding attributes.
22
+
23
+ v0.7.1b Removed debugging statement that was being output.
24
+
25
+ v0.7.0b Use of [] in input names now allowed, for grouping inputs like an array.
26
+
27
+ v0.6.14b Lowercased aria.rb for bundler.
28
+
29
+ v0.6.12b Another mistake, an accidental move of a file - this will teach me to run the specs even for trivial changes!
30
+
31
+ v0.6.11b My mistake this time, not using lowercase for the file names was causing a problem deploying to Heroku.
32
+
33
+ v0.6.10b Removed ENV vars from gemspec as deployment to Heroku with Bundler is problematic.
34
+
35
+ v0.6.7b Added `describe` method, via the Aria plugin. It will help to keep the forms accessible.
36
+
37
+ v0.6.6b Validation rules are now built. Validations for required, digits and maxlength added.
38
+
39
+ v0.6.5b Labels get the "required" class too when using JQueryValidation, to make all our styling wishes come true!
40
+
41
+ v0.6.4b Default option for select tags now has its id attribute appended with "_default" to stop the id clashing with the select tag.
42
+
43
+ v0.6.3b Convenience method `submit` is now more convenient as it takes the value as part of the hash args without a pesky unused parameter getting in the way.
44
+
45
+ v0.6.2b Added easier labelling for textareas.
46
+
47
+ v0.6.1b The function for the JQuery validation wasn't right, fixed it now, and updated the specs a little for it.
48
+
49
+ v0.6.0b All form fields get an id. Added "hidden" input convenience method.
50
+
51
+ v0.5.2b Turns out that you can learn a lot if you run the specs, such as inners didn't need changing to default_proc, just atts.
52
+
53
+ v0.5.1b Hash defaults for form should work better now as moved to using Hash#default_proc which updates the key the way required.
54
+
55
+ v0.5.0b Added a JQuery validation plugin.
56
+
57
+ v0.4.0b Added an outputter class, currently hidden under old API. Improved argument passing, added before and after hooks for output. Extra args to Campo.output are now passed as options, and partials are done this way too instead of the clumsy prepended argument.
58
+
59
+ v0.3.6b removed need for block variables.
60
+
61
+ v0.3.5b Changed tabindex to use an instance variable, so now forms should be able to be nested and the tabindex update properly.
62
+
1
63
  v0.3.4 Added `password` convenience method.
2
64
 
3
65
  v0.3.2 Blocks are now passed on for .literal and .bit_of_ruby
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source "https://rubygems.org"
2
+
3
+ group :development do
4
+ gem "rake"
5
+ gem "yard"
6
+ gem "maruku"
7
+ gem "wirble"
8
+ gem "reek"
9
+ end
10
+
11
+ group :testing do
12
+ gem "rspec"
13
+ gem "simplecov"
14
+ gem "haml"
15
+ end
data/README.markdown CHANGED
@@ -1,3 +1,5 @@
1
+ [![Build Status for development branch](https://secure.travis-ci.org/yb66/Campo.png?branch=develop)](http://travis-ci.org/yb66/Campo)
2
+
1
3
  # Campo #
2
4
 
3
5
  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. [See Haml docs for more](http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#attribute_methods). More on that below.
@@ -6,8 +8,15 @@ Btw, I'll be using this with Sinatra, if you're using Rails you'll need to work
6
8
 
7
9
  ## Note! ##
8
10
 
9
- As always, keep in mind this is an open source project (licence below) and you can contribute! If you find a problem or would like a feature changed or added, let me know, or even better, fork the project and send me a pull request.
11
+ As always, keep in mind this is an open source project (licence below) and you can contribute! If you find a problem or would like a feature changed or added, let me know, or even better, fork the project and send me a pull request. See the "Contributing" section for some notes on how to do that.
12
+
13
+ ## Double note! ##
14
+
15
+ I use Campo myself, and I'm trying to improve it. As I don't want to push new stuff out before I've had a chance to give it a whirl and see if it makes sense and works (through experience, specs aren't everything) I'll have several versions of this up here, some unreleased. I tend to append a 'b' to the end of an unreleased version. Please make sure you're reading the documentation for the version you're using!
10
16
 
17
+ ## Version numbers ##
18
+
19
+ You'll notice this library is well past version 0.0.1. Some people take this to mean something like "it works brilliantly", but in fact, I'm attempting to use the [semver standard](http://semver.org/). In essence, it tells you about changes to the API, not about code quality - that's what the specs/tests are for. It's worth a read.
11
20
 
12
21
  ## Why write this? ##
13
22
 
@@ -22,124 +31,79 @@ Here's an example form:
22
31
 
23
32
  # Now starts the real action #
24
33
 
25
- form = Campo.form "myform", action: "/my/form/update/"
26
-
27
- form.fieldset "Your details" do |f|
28
- f.text "full_name", size: 60
29
- f.text "dob", "Date of birth: ", size: 8
30
-
31
- f.select( "gender_id", {opts: genders }).with_default.labelled( "Gender: " )
32
-
33
- f.select "teas" do |s|
34
- s.with_default
35
- s.option("ceylon")
36
- s.option("breakfast")
37
- s.option("earl grey")
38
- s.option("oolong")
39
- s.option("sencha")
40
- end.labelled "Favourite tea:"
41
-
42
- f.text "occupation", "Occupation: ", size: 60
43
- f.text "phone_landline", "Phone landline : ", size: 20
44
- f.text "phone_mobile", "Phone mobile : ", size: 20
45
-
46
- f.submit "Save"
47
-
34
+ form = Campo.form "personal_details", action: %Q!"uri("/my/personal_details/update/")! do
35
+ fieldset "Your details" do
36
+ text "full_name", "Full name: ", size: 60
37
+ text "dob", "Date of birth: ", size: 10 #TODO change this
38
+ fieldset "Gender: " do
39
+ radio "gender", "Male", value: 1
40
+ radio "gender", "Female", value: 2
41
+ end
42
+ select("teas").with_default.option("ceylon").option("breakfast").option("earl grey").labelled("Favourite tea:")
43
+ text "occupation", "Occupation: ", size: 60
44
+ text "phone_landline", "Phone (landline): ", size: 20
45
+ text "phone_mobile", "Phone (mobile): ", size: 20
46
+ fieldset "May we contact you..." do
47
+ checkbox "contactable", "In the day?", value: "day"
48
+ checkbox "contactable", "In the evening?", value: "evening"
49
+ end
50
+ submit "Save"
51
+ end
48
52
  end
53
+
54
+
55
+ puts Campo.output( form )
49
56
 
50
57
  and the output:
51
58
 
52
-
53
- puts Campo.output( form )
54
-
55
59
  - atts = {} if atts.nil?
56
- - atts.default = {} if atts.default.nil?
60
+ - atts.default_proc = proc {|hash, key| hash[key] = {} } if atts.default_proc.nil?
61
+ - inners = {} if inners.nil?
57
62
  - inners.default = "" if inners.default.nil?
58
- - i = 0 # for tabindex
59
-
60
- %form{ atts[:myform], method: "POST", action: "/my/form/update/", name: "myform", }
63
+ - @campo_tabindex ||= 0 # for tabindex
64
+ %form{ atts[:personal_details], id: "personal_details", method: "POST", action: uri("/my/personal_details/update/"), name: "personal_details", }
61
65
  %fieldset{ }
62
66
  %legend{ }Your details
63
67
  %label{ for: "full_name", }
64
68
  Full name:
65
- %input{ atts[:full_name], tabindex: "#{i += 1}", type: "text", id: "full_name", size: "60", name: "full_name", }
69
+ %input{ atts[:full_name], tabindex: "#{@campo_tabindex += 1}", id: "full_name", type: "text", size: "60", name: "full_name", }
66
70
  %label{ for: "dob", }
67
71
  Date of birth:
68
- %input{ atts[:dob], tabindex: "#{i += 1}", type: "text", id: "dob", size: "8", name: "dob", }
69
- %label{ for: "gender_id", }
70
- Gender:
71
- %select{ atts[:gender_id], tabindex: "#{i += 1}", name: "gender_id", }
72
- %option{ value: "", disabled: "disabled", name: "gender_id", }Choose one:
73
- %option{ atts[:gender_id_1], value: "1", id: "gender_id_1", name: "gender_id", }Male
74
- %option{ atts[:gender_id_2], value: "2", id: "gender_id_2", name: "gender_id", }Female
72
+ %input{ atts[:dob], tabindex: "#{@campo_tabindex += 1}", id: "dob", type: "text", size: "10", name: "dob", }
73
+ %fieldset{ }
74
+ %legend{ }Gender:
75
+ %label{ for: "gender_1", }
76
+ Male
77
+ %input{ atts[:gender_1], tabindex: "#{@campo_tabindex += 1}", id: "gender_1", type: "radio", value: "1", name: "gender", }
78
+ %label{ for: "gender_2", }
79
+ Female
80
+ %input{ atts[:gender_2], tabindex: "#{@campo_tabindex += 1}", id: "gender_2", type: "radio", value: "2", name: "gender", }
75
81
  %label{ for: "teas", }
76
82
  Favourite tea:
77
- %select{ atts[:teas], tabindex: "#{i += 1}", name: "teas", }
78
- %option{ value: "", disabled: "disabled", name: "teas", }Choose one:
79
- %option{ atts[:teas_ceylon], value: "ceylon", id: "teas_ceylon", name: "teas", }Ceylon
80
- %option{ atts[:teas_breakfast], value: "breakfast", id: "teas_breakfast", name: "teas", }Breakfast
81
- %option{ atts[:teas_earl_grey], value: "earl grey", id: "teas_earl_grey", name: "teas", }Earl grey
82
- %option{ atts[:teas_oolong], value: "oolong", id: "teas_oolong", name: "teas", }Oolong
83
- %option{ atts[:teas_sencha], value: "sencha", id: "teas_sencha", name: "teas", }Sencha
83
+ %select{ atts[:teas], tabindex: "#{@campo_tabindex += 1}", id: "teas", name: "teas", }
84
+ %option{ atts[:teas], id: "teas", value: "", disabled: "disabled", name: "teas", }Choose one:
85
+ %option{ atts[:teas_ceylon], id: "teas_ceylon", value: "ceylon", name: "teas", }Ceylon
86
+ %option{ atts[:teas_breakfast], id: "teas_breakfast", value: "breakfast", name: "teas", }Breakfast
87
+ %option{ atts[:teas_earl_grey], id: "teas_earl_grey", value: "earl grey", name: "teas", }Earl grey
84
88
  %label{ for: "occupation", }
85
89
  Occupation:
86
- %input{ atts[:occupation], tabindex: "#{i += 1}", type: "text", id: "occupation", size: "60", name: "occupation", }
90
+ %input{ atts[:occupation], tabindex: "#{@campo_tabindex += 1}", id: "occupation", type: "text", size: "60", name: "occupation", }
87
91
  %label{ for: "phone_landline", }
88
92
  Phone (landline):
89
- %input{ atts[:phone_landline], tabindex: "#{i += 1}", type: "text", id: "phone_landline", size: "20", name: "phone_landline", }
93
+ %input{ atts[:phone_landline], tabindex: "#{@campo_tabindex += 1}", id: "phone_landline", type: "text", size: "20", name: "phone_landline", }
90
94
  %label{ for: "phone_mobile", }
91
95
  Phone (mobile):
92
- %input{ atts[:phone_mobile], tabindex: "#{i += 1}", type: "text", id: "phone_mobile", size: "20", name: "phone_mobile", }
93
- %input{ atts[:Save_Save], tabindex: "#{i += 1}", type: "submit", id: "Save_Save", value: "Save", }
94
-
95
- and that outputs:
96
-
97
- puts Haml::Engine.new( Campo.output( form ) ).render
98
-
99
- <form action='/my/form/update/' method='POST' name='myform'>
100
- <fieldset>
101
- <legend>Your details</legend>
102
- <label for='full_name'>
103
- Full name:
104
- <input id='full_name' name='full_name' size='60' tabindex='1' type='text' />
105
- </label>
106
- Date of birth:
107
- <input id='dob' name='dob' size='8' tabindex='2' type='text' />
108
- </label>
109
- <label for='gender_id'>
110
- Gender:
111
- <select name='gender_id' tabindex='3'>
112
- <option disabled='disabled' name='gender_id' value=''>Choose one:</option>
113
- <option id='gender_id_1' name='gender_id' value='1'>Male</option>
114
- <option id='gender_id_2' name='gender_id' value='2'>Female</option>
115
- </select>
116
- </label>
117
- <label for='teas'>
118
- Favourite tea:
119
- <select name='teas' tabindex='4'>
120
- <option disabled='disabled' name='teas' value=''>Choose one:</option>
121
- <option id='teas_ceylon' name='teas' value='ceylon'>Ceylon</option>
122
- <option id='teas_breakfast' name='teas' value='breakfast'>Breakfast</option>
123
- <option id='teas_earl_grey' name='teas' value='earl grey'>Earl grey</option>
124
- <option id='teas_oolong' name='teas' value='oolong'>Oolong</option>
125
- <option id='teas_sencha' name='teas' value='sencha'>Sencha</option>
126
- </select>
127
- </label>
128
- <label for='occupation'>
129
- Occupation:
130
- <input id='occupation' name='occupation' size='60' tabindex='5' type='text' />
131
- </label>
132
- <label for='phone_landline'>
133
- Phone (landline):
134
- <input id='phone_landline' name='phone_landline' size='20' tabindex='6' type='text' />
135
- </label>
136
- <label for='phone_mobile'>
137
- Phone (mobile):
138
- <input id='phone_mobile' name='phone_mobile' size='20' tabindex='7' type='text' />
139
- </label>
140
- <input id='Save_Save' tabindex='8' type='submit' value='Save' />
141
- </fieldset>
142
- </form>
96
+ %input{ atts[:phone_mobile], tabindex: "#{@campo_tabindex += 1}", id: "phone_mobile", type: "text", size: "20", name: "phone_mobile", }
97
+ %fieldset{ }
98
+ %legend{ }May we contact you...
99
+ %label{ for: "contactable_day", }
100
+ In the day?
101
+ %input{ atts[:contactable_day], tabindex: "#{@campo_tabindex += 1}", id: "contactable_day", type: "checkbox", value: "day", name: "contactable", }
102
+ %label{ for: "contactable_evening", }
103
+ In the evening?
104
+ %input{ atts[:contactable_evening], tabindex: "#{@campo_tabindex += 1}", id: "contactable_evening", type: "checkbox", value: "evening", name: "contactable", }
105
+ %input{ atts[:Save], tabindex: "#{@campo_tabindex += 1}", id: "Save", type: "submit", value: "Save", }
106
+
143
107
 
144
108
  ## Haml attributes ##
145
109
 
@@ -151,65 +115,82 @@ These get added to the top when calling `Campo.output`, to provide sane defaults
151
115
  - atts.default = {} if atts.default.nil?
152
116
  - inners = {} if inners.nil?
153
117
  - inners.default = "" if inners.default.nil?
118
+ - @campo_tabindex ||= 0 # for tabindex
154
119
 
155
120
  Note: if you don't want these added, you can do:
156
121
 
157
- Campo.output :partial, your_tag
122
+ Campo.output name-of-tag-here, :partial => true
158
123
 
124
+ e.g
159
125
 
160
- 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.
126
+ Campo.output form, :partial => true
127
+
161
128
 
162
129
  Here's some Campo code for a select tag with options:
163
130
 
164
- form = Campo.form "best_bands", action: "/best/bands/" do |form|
165
- form.select("bands").option("Suede").option("Blur").option("Oasis").option("Echobelly").option("Pulp").option("Supergrass").with_default.labelled("Favourite band:")
131
+ form = Campo.form "best_bands", action: "/best/bands/" do
132
+ select("bands").option("Suede").option("Blur").option("Oasis").option("Echobelly").option("Pulp").option("Supergrass").with_default.labelled("Favourite band:")
166
133
  end
167
134
 
168
135
  or
169
136
 
170
- form = Campo.form "best_bands", action: "/best/bands/"
171
- form.select("bands") do |s|
172
- s.with_default
173
- s.option("Suede")
174
- s.option("Blur")
175
- s.option("Oasis")
176
- s.option("Echobelly")
177
- s.option("Pulp")
178
- s.option("Supergrass")
179
- end.labelled("Favourite band:")
137
+ form = Campo.form "best_bands", action: "/best/bands/" do
138
+ select "bands" do
139
+ with_default
140
+ option "Suede"
141
+ option "Blur"
142
+ option "Oasis"
143
+ option "Echobelly"
144
+ option "Pulp"
145
+ option "Supergrass"
146
+ end.labelled("Favourite band:")
147
+ end
180
148
 
181
149
  or an array of arrays:
182
150
 
183
- form = Campo.form "best_bands", action: "/best/bands/"
184
- form.select( "bands", opts: [
185
- ["Suede"],
186
- ["Blur"],
187
- ["Oasis"],
188
- ["Echobelly"],
189
- ["Pulp"],
190
- ["Supergrass"],
191
- ] ).with_default.labelled("Favourite band:")
192
-
193
- (or mix and match blocks, .new, arrays and hashes)
151
+ form = Campo.form "best_bands", action: "/best/bands/" do
152
+ select( "bands", opts: [
153
+ ["Suede"],
154
+ ["Blur"],
155
+ ["Oasis"],
156
+ ["Echobelly"],
157
+ ["Pulp"],
158
+ ["Supergrass"],
159
+ ] ).with_default.labelled("Favourite band:")
160
+ end
161
+
162
+ or mix and match blocks, .new, arrays and hashes as you see fit.
194
163
 
195
164
 
196
165
  Generate the haml:
197
166
 
198
- Campo.output form #
167
+ Campo.output form
199
168
 
200
169
  And the Haml generated:
201
170
 
202
- %form{ atts[:best_bands], method: "POST", action: "/best/bands/", name: "best_bands", }
171
+ - atts = {} if atts.nil?
172
+ - atts.default_proc = proc {|hash, key| hash[key] = {} } if atts.default_proc.nil?
173
+ - inners = {} if inners.nil?
174
+ - inners.default = "" if inners.default.nil?
175
+ - @campo_tabindex ||= 0 # for tabindex
176
+ %form{ atts[:best_bands], id: "best_bands", method: "POST", action: "/best/bands/", name: "best_bands", }
203
177
  %label{ for: "bands", }
204
178
  Favourite band:
205
- %select{ atts[:bands], tabindex: "#{i += 1}", name: "bands", }
206
- %option{ value: "", disabled: "disabled", name: "bands", }Choose one:
207
- %option{ atts[:bands_suede], value: "Suede", id: "bands_suede", name: "bands", }Suede
208
- %option{ atts[:bands_blur], value: "Blur", id: "bands_blur", name: "bands", }Blur
209
- %option{ atts[:bands_oasis], value: "Oasis", id: "bands_oasis", name: "bands", }Oasis
210
- %option{ atts[:bands_echobelly], value: "Echobelly", id: "bands_echobelly", name: "bands", }Echobelly
211
- %option{ atts[:bands_pulp], value: "Pulp", id: "bands_pulp", name: "bands", }Pulp
212
- %option{ atts[:bands_supergrass], value: "Supergrass", id: "bands_supergrass", name: "bands", }Supergrass
179
+ %select{ atts[:bands], tabindex: "#{@campo_tabindex += 1}", id: "bands", name: "bands", }
180
+ %option{ atts[:bands], id: "bands", value: "", disabled: "disabled", name: "bands", }Choose one:
181
+ %option{ atts[:bands_suede], id: "bands_suede", value: "Suede", name: "bands", }Suede
182
+ %option{ atts[:bands_blur], id: "bands_blur", value: "Blur", name: "bands", }Blur
183
+ %option{ atts[:bands_oasis], id: "bands_oasis", value: "Oasis", name: "bands", }Oasis
184
+ %option{ atts[:bands_echobelly], id: "bands_echobelly", value: "Echobelly", name: "bands", }Echobelly
185
+ %option{ atts[:bands_pulp], id: "bands_pulp", value: "Pulp", name: "bands", }Pulp
186
+ %option{ atts[:bands_supergrass], id: "bands_supergrass", value: "Supergrass", name: "bands", }Supergrass
187
+
188
+
189
+ In the examples above, notice how the output for each tag gets a local variable added to the front.
190
+
191
+ > %option{ **atts[:bands_supergrass]**, id: "bands_supergrass", value: "Supergrass", name: "bands", }Supergrass
192
+
193
+ You can either fill that variable with a hash pair, or an empty hash gets passed and nothing happens. Read the Haml docs link at the top of the readme for more info.
213
194
 
214
195
 
215
196
  If you wanted to select "Blur" dynamically (and you should, but I'd accept Suede) you might do:
@@ -229,6 +210,11 @@ You can do this with any kind of attribute you wish to add. For example:
229
210
 
230
211
  atts[:bands_blur] = {not_worth_listening_to: "selected"}
231
212
 
213
+ #=> <option not_worth_listening_to='selected' id='bands_blur' name='bands' value='Blur'>Blur</option>
214
+
215
+ But I doubt your name is Noel Gallagher, which makes this a spurious example.
216
+
217
+
232
218
  ## Be selective ##
233
219
 
234
220
  opts = [
@@ -245,16 +231,15 @@ You can do this with any kind of attribute you wish to add. For example:
245
231
  Output:
246
232
 
247
233
  - atts = {} if atts.nil?
248
- - atts.default = {} if atts.default.nil?
234
+ - atts.default_proc = proc {|hash, key| hash[key] = {} } if atts.default_proc.nil?
249
235
  - inners = {} if inners.nil?
250
236
  - inners.default = "" if inners.default.nil?
251
- - i = 0 # for tabindex
252
-
253
- %form{ atts[:selective_example], method: "POST", name: "selective_example", }
254
- %select{ atts[:teas], tabindex: "#{i += 1}", name: "teas", }
255
- %option{ atts[:teas_ceylon], value: "ceylon", id: "teas_ceylon", name: "teas", }Ceylon
256
- %option{ atts[:teas_english_breakfast], value: "english_breakfast", selected: "selected", id: "teas_english_breakfast", name: "teas", }English breakfast
257
- %option{ atts[:teas_earl_grey], value: "earl_grey", id: "teas_earl_grey", name: "teas", }Earl grey
237
+ - @campo_tabindex ||= 0 # for tabindex
238
+ %form{ atts[:selective_example], id: "selective_example", method: "POST", name: "selective_example", }
239
+ %select{ atts[:teas], tabindex: "#{@campo_tabindex += 1}", id: "teas", name: "teas", }
240
+ %option{ atts[:teas_ceylon], id: "teas_ceylon", value: "ceylon", name: "teas", }Ceylon
241
+ %option{ atts[:teas_english_breakfast], id: "teas_english_breakfast", value: "english_breakfast", selected: "selected", name: "teas", }English breakfast
242
+ %option{ atts[:teas_earl_grey], id: "teas_earl_grey", value: "earl_grey", name: "teas", }Earl grey
258
243
 
259
244
  ## Pass a hash ##
260
245
 
@@ -268,15 +253,15 @@ Output:
268
253
  form = Campo.form "simple_hash_example"
269
254
  form.select "teas", opts: opts
270
255
 
271
- Campo.output :partial, form
256
+ Campo.output form, partial: true
272
257
 
273
258
  Output:
274
259
 
275
- %form{ atts[:simple_hash_example], method: "POST", name: "simple_hash_example", }
276
- %select{ atts[:teas], tabindex: "#{i += 1}", name: "teas", }
277
- %option{ atts[:teas_ceylon], value: "ceylon", id: "teas_ceylon", name: "teas", }Ceylon
278
- %option{ atts[:teas_english_breakfast], value: "english_breakfast", id: "teas_english_breakfast", name: "teas", }English Breakfast
279
- %option{ atts[:teas_earl_grey], value: "earl_grey", id: "teas_earl_grey", name: "teas", }Earl Grey
260
+ %form{ atts[:simple_hash_example], id: "simple_hash_example", method: "POST", name: "simple_hash_example", }
261
+ %select{ atts[:teas], tabindex: "#{@campo_tabindex += 1}", id: "teas", name: "teas", }
262
+ %option{ atts[:teas_ceylon], id: "teas_ceylon", value: "ceylon", name: "teas", }Ceylon
263
+ %option{ atts[:teas_english_breakfast], id: "teas_english_breakfast", value: "english_breakfast", name: "teas", }English Breakfast
264
+ %option{ atts[:teas_earl_grey], id: "teas_earl_grey", value: "earl_grey", name: "teas", }Earl Grey
280
265
 
281
266
  With an array for the value:
282
267
 
@@ -289,15 +274,15 @@ With an array for the value:
289
274
  form = Campo.form "hash_with_array_example"
290
275
  form.select "teas", opts: opts
291
276
 
292
- Campo.output :partial, form
277
+ Campo.output form, :partial => true
293
278
 
294
279
  Output:
295
280
 
296
- %form{ atts[:hash_with_array_example], method: "POST", name: "hash_with_array_example", }
297
- %select{ atts[:teas], tabindex: "#{i += 1}", name: "teas", }
298
- %option{ atts[:teas_ceylon], value: "ceylon", id: "teas_ceylon", name: "teas", }Ceylon
299
- %option{ atts[:teas_english_breakfast], value: "english_breakfast", selected: "selected", id: "teas_english_breakfast", name: "teas", }English Breakfast
300
- %option{ atts[:teas_earl_grey], value: "earl_grey", id: "teas_earl_grey", name: "teas", }Earl Grey
281
+ %form{ atts[:hash_with_array_example], id: "hash_with_array_example", method: "POST", name: "hash_with_array_example", }
282
+ %select{ atts[:teas], tabindex: "#{@campo_tabindex += 1}", id: "teas", name: "teas", }
283
+ %option{ atts[:teas_ceylon], id: "teas_ceylon", value: "ceylon", name: "teas", }Ceylon
284
+ %option{ atts[:teas_english_breakfast], id: "teas_english_breakfast", value: "english_breakfast", selected: "selected", name: "teas", }English Breakfast
285
+ %option{ atts[:teas_earl_grey], id: "teas_earl_grey", value: "earl_grey", name: "teas", }Earl Grey
301
286
 
302
287
  ## Adding in helpers ##
303
288
 
@@ -322,40 +307,30 @@ Although, if you forget the "=" sign it will add it for you.
322
307
 
323
308
  ## And literals ##
324
309
 
325
- It's really just a literal:
310
+ `bit_of_ruby` is really just a literal with a shortcut. Here are some examples using literals:
326
311
 
327
312
 
328
- form = Campo.form "favourite_teas", action: %Q!"uri("/fav/teas/")! do |form|
329
- form.select("teas").with_default.option("ceylon").option("breakfast").option("earl grey").labelled("Favourite tea:")
330
- form.literal %Q<%p= "I like tea!">
313
+ form = Campo.form "favourite_teas", action: %Q!"uri("/fav/teas/")! do
314
+ select("teas").with_default.option("ceylon").option("breakfast").option("earl grey").labelled("Favourite tea:")
315
+ literal %Q<%p= "I like tea!">
331
316
  end
332
- Campo.output form
317
+
318
+ puts Campo.output form
333
319
 
334
-
335
- %form{ atts[:favourite_teas], method: "POST", action: uri("/fav/teas/"), name: "favourite_teas", }
320
+ - atts = {} if atts.nil?
321
+ - atts.default_proc = proc {|hash, key| hash[key] = {} } if atts.default_proc.nil?
322
+ - inners = {} if inners.nil?
323
+ - inners.default = "" if inners.default.nil?
324
+ - @campo_tabindex ||= 0 # for tabindex
325
+ %form{ atts[:favourite_teas], id: "favourite_teas", method: "POST", action: uri("/fav/teas/"), name: "favourite_teas", }
336
326
  %label{ for: "teas", }
337
327
  Favourite tea:
338
- %select{ atts[:teas], tabindex: "#{i += 1}", name: "teas", }
339
- %option{ value: "", disabled: "disabled", name: "teas", }Choose one:
340
- %option{ atts[:teas_ceylon], value: "ceylon", id: "teas_ceylon", name: "teas", }Ceylon
341
- %option{ atts[:teas_breakfast], value: "breakfast", id: "teas_breakfast", name: "teas", }Breakfast
342
- %option{ atts[:teas_earl_grey], value: "earl grey", id: "teas_earl_grey", name: "teas", }Earl grey
328
+ %select{ atts[:teas], tabindex: "#{@campo_tabindex += 1}", id: "teas", name: "teas", }
329
+ %option{ atts[:teas], id: "teas", value: "", disabled: "disabled", name: "teas", }Choose one:
330
+ %option{ atts[:teas_ceylon], id: "teas_ceylon", value: "ceylon", name: "teas", }Ceylon
331
+ %option{ atts[:teas_breakfast], id: "teas_breakfast", value: "breakfast", name: "teas", }Breakfast
332
+ %option{ atts[:teas_earl_grey], id: "teas_earl_grey", value: "earl grey", name: "teas", }Earl grey
343
333
  %p= "I like tea!"
344
-
345
- puts Haml::Engine.new( Campo.output form ).render
346
-
347
- <form action='/fav/teas/' method='POST' name='favourite_teas'>
348
- <label for='teas'>
349
- Favourite tea:
350
- <select name='teas' tabindex='1'>
351
- <option disabled='disabled' name='teas' value=''>Choose one:</option>
352
- <option id='teas_ceylon' name='teas' value='ceylon'>Ceylon</option>
353
- <option id='teas_breakfast' name='teas' value='breakfast'>Breakfast</option>
354
- <option id='teas_earl_grey' name='teas' value='earl grey'>Earl grey</option>
355
- </select>
356
- </label>
357
- <p>I like tea!</p>
358
- </form>
359
334
 
360
335
  You can use literals to wrap forms in divs too:
361
336
 
@@ -363,46 +338,177 @@ You can use literals to wrap forms in divs too:
363
338
  wrapper << form # the form defined already above
364
339
  end
365
340
 
366
- puts Campo.output doc
367
-
341
+ puts Campo.output doc, partial: true
342
+
368
343
  .centred.form
369
- %form{ atts[:favourite_teas], method: "POST", action: uri("/fav/teas/"), name: "favourite_teas", }
344
+ %form{ atts[:favourite_teas], id: "favourite_teas", method: "POST", action: uri("/fav/teas/"), name: "favourite_teas", }
370
345
  %label{ for: "teas", }
371
346
  Favourite tea:
372
- %select{ atts[:teas], tabindex: "#{i += 1}", name: "teas", }
373
- %option{ value: "", disabled: "disabled", name: "teas", }Choose one:
374
- %option{ atts[:teas_ceylon], value: "ceylon", id: "teas_ceylon", name: "teas", }Ceylon
375
- %option{ atts[:teas_breakfast], value: "breakfast", id: "teas_breakfast", name: "teas", }Breakfast
376
- %option{ atts[:teas_earl_grey], value: "earl grey", id: "teas_earl_grey", name: "teas", }Earl grey
347
+ %select{ atts[:teas], tabindex: "#{@campo_tabindex += 1}", id: "teas", name: "teas", }
348
+ %option{ atts[:teas], id: "teas", value: "", disabled: "disabled", name: "teas", }Choose one:
349
+ %option{ atts[:teas_ceylon], id: "teas_ceylon", value: "ceylon", name: "teas", }Ceylon
350
+ %option{ atts[:teas_breakfast], id: "teas_breakfast", value: "breakfast", name: "teas", }Breakfast
351
+ %option{ atts[:teas_earl_grey], id: "teas_earl_grey", value: "earl grey", name: "teas", }Earl grey
377
352
  %p= "I like tea!"
353
+
378
354
 
379
355
  ## tabindex ##
380
356
 
381
- Each field gets `tabindex: "#{i += 1}"` added to its attributes. This will generate a tabindex easily for you.
357
+ Each field gets `@campo_tabindex += 1` added to its attributes. This will generate a tabindex easily for you.
382
358
 
383
- Note: I'm considering making this an instance variable so that it can be passed through easily to nested partials, but I need to test it first.
359
+ Since it's an instance variable it can be passed through easily to nested partials and the count will still be right.
384
360
 
385
361
  ## Blocks ##
386
362
 
387
- Most fields will accept a block, so you can nest whatever you like. Generally I just use this for forms, fieldsets and selects (and those have specs) but if you want to try something new, do it! Let me know if it breaks.
363
+ Most fields will accept a block, so you can nest whatever you like. Generally I just use this for forms, fieldsets and selects (and those have specs) but if you want to try something new, do it! Let me know if it breaks. You don't have to use the `|var|` notation unless you feel it's helpful.
388
364
 
389
365
  form = Campo.literal "%div" do |div|
390
- div << Campo.form( "nested" ) do |form|
391
- form.fieldset do |f|
392
- f.select "blurg" do |s|
393
- s.option "oopsie"
394
- s.option "daisies"
366
+ div << Campo.form( "nested" ) do
367
+ fieldset "Nest" do
368
+ select "blurg" do
369
+ option "oopsie"
370
+ option "daisies"
395
371
  end.labelled "splat"
396
- f.text "blah"
372
+ text "blah"
397
373
  end
398
374
  end
399
375
  end
400
376
 
377
+ puts Campo.output form
378
+
379
+ Output:
380
+
381
+ - atts = {} if atts.nil?
382
+ - atts.default_proc = proc {|hash, key| hash[key] = {} } if atts.default_proc.nil?
383
+ - inners = {} if inners.nil?
384
+ - inners.default = "" if inners.default.nil?
385
+ - @campo_tabindex ||= 0 # for tabindex
386
+ %div
387
+ %form{ atts[:nested], id: "nested", method: "POST", name: "nested", }
388
+ %fieldset{ }
389
+ %legend{ }Nest
390
+ %label{ for: "blurg", }
391
+ splat
392
+ %select{ atts[:blurg], tabindex: "#{@campo_tabindex += 1}", id: "blurg", name: "blurg", }
393
+ %option{ atts[:blurg_oopsie], id: "blurg_oopsie", value: "oopsie", name: "blurg", }Oopsie
394
+ %option{ atts[:blurg_daisies], id: "blurg_daisies", value: "daisies", name: "blurg", }Daisies
395
+ %label{ for: "blah", }
396
+ Blah
397
+ %input{ atts[:blah], tabindex: "#{@campo_tabindex += 1}", id: "blah", type: "text", name: "blah", }
398
+
399
+ ## Plugins ##
400
+
401
+ I've written a couple of plugins. If you wish to write one yourself you'll need to look for the code for now until I write some proper instructions.
402
+
403
+ To load a plugin called "Aria":
404
+
405
+ Campo.plugin :Aria
406
+
407
+ ### Aria ###
408
+
409
+ Helpful methods for outputting forms that add in the [WAI-ARIA](http://www.w3.org/WAI/intro/aria) attributes into your forms.
410
+
411
+ Here's an example of how I've used this with an account registration form to provide information about each field:
412
+
413
+ require 'campo'
414
+
415
+ Campo.plugin :Aria
416
+
417
+ div = Campo.literal "#session.form" do
418
+ self << Campo.form( "register" ) do
419
+ fieldset "Register" do
420
+ literal "%p.warning" do
421
+ literal "Fields marked with an <em>*</em> are required."
422
+ end
423
+ text( "email_address", size: 40 ).describe("A valid email address, please.", class: "validation info description")
424
+ text( "username", size: 40 ).describe([["Must be 3 characters at least.", {class: 'validate_username required', id: 'username_length'}], ["No spaces or punctuation, only numbers, letters and underscores, please.", {class: 'validate_username required', id: 'username_specialchars'}], ["You may not use your email address as a username.", {class: 'validate_username required', id: 'username_not_email_address'}]], class: "validation info description")
425
+ password( "password", size: 40 ).describe([["Must be 8 characters at least.",{class: 'validate_password required', id: 'password_length'}],["It's better to add some numbers/punctuation.", class: 'validate_password', id: 'password_specialchars'],["You may not use your email address or username as a password.", {class: 'validate_password required', id: 'password_not_email_address'}],["For strength, try to make it a phrase, and not to be something you've previously used.",{}]], class: "validation info description")
426
+ bit_of_ruby "= Rack::Csrf.tag(env)"
427
+ literal "#validation_overall_result.validation.hidden" do
428
+ literal "There were problems with the form entries. Please check the highlighted fields."
429
+ end
430
+ submit
431
+ end
432
+ end
433
+ end
434
+
435
+
436
+
437
+ puts Campo.output div
438
+
439
+ Now let's take a look at the output of the email_address field:
440
+
441
+ %label{ for: "email_address", }
442
+ Email address
443
+ %span{id: "email_address_description", class: "validation info description", }
444
+ A valid email address, please.
445
+ %input{ atts[:email_address], tabindex: "#{@campo_tabindex += 1}", id: "email_address", type: "text", size: "40", name: "email_address", :"aria-describedby" => "email_address_description", }
446
+
447
+ Notice how the label contains a span that precedes the input field - this is helpful for screen readers because those that aren't using ARIA will still hit the description. The input field refers to this span using the aria-describedby attribute.
448
+
449
+ For a more complex example, look at the output for username:
450
+
451
+ %label{ for: "username", }
452
+ Username
453
+ %span{id: "username_description", class: "validation info description", }
454
+ %ul
455
+ %li{ id: "username_length", class: "validate_username required", }
456
+ Must be 3 characters at least.
457
+ %li{ id: "username_specialchars", class: "validate_username required", }
458
+ No spaces or punctuation, only numbers, letters and underscores, please.
459
+ %li{ id: "username_not_email_address", class: "validate_username required", }
460
+ You may not use your email address as a username.
461
+ %input{ atts[:username], tabindex: "#{@campo_tabindex += 1}", id: "username", type: "text", size: "40", name: "username", :"aria-describedby" => "username_description", }
462
+
463
+ For the campo code, we added an array of tuples to the `describe` method. These tuples are then made into an unordered list within the span. The first part of each tuple is the text description, the second part any attributes you with the list item to have. I've used this in a project in conjunction with JQuery to produce dynamic information back to the user of the form. You don't have to pass the second part of the tuple, in other words you can do:
464
+
465
+ text("d").describe([["You"], ["Me"], ["Them"]])
466
+
467
+ But I would usually expect that you'd want to pass each item an id. It's up to you.
468
+
469
+ ### Contributing ###
470
+
471
+ When contributings, most of all, remember that **any** contribution you can make will be valuable, whether that is putting in a ticket for a feature request (or a bug, but they don't happen here;), cleaning up some grammar, writing some documentation (or even a blog post, let me know!) or a full blooded piece of code - it's **all** welcome and encouraged.
472
+
473
+ To contribute some code:
474
+
475
+ 1. Fork this, then:
476
+ * `git clone git@github.com:YOUR-USERNAME/Campo.git`
477
+ * `git remote add upstream git@github.com:yb66/Campo.git`
478
+ * `git fetch upstream`
479
+ * `git checkout develop`
480
+ * Decide on the feature you wish to add.
481
+ - Give it a snazzy name, such as **kitchen_sink**.
482
+ - `git checkout -b kitchen_sink`
483
+ * Install Bundler.
484
+ - `gem install bundler -r --no-ri --no-rdoc`
485
+ * Install gems from Gemfile.
486
+ - `bundle install --binstubs --path vendor`
487
+ - Any further updates needed, just run `bundle install`, it'll remember the rest.
488
+ * Write some specs.
489
+ * Write some code. (Yes, I believe that is the correct order, and you'll never find me doing any different;)
490
+ * Write some documentation using Yard comments - see [Yard's Getting Started](http://rubydoc.info/docs/yard/file/docs/GettingStarted.md)
491
+ - Use real English (i.e. full stops and commas, no l33t or LOLZ). I'll accept American English even though it's ugly. Don't be surprised if I 'correct' it.
492
+ - Code without comments won't get in, I don't have the time to work out what you've done if you're not prepared to spend some time telling me (and everyone else).
493
+ * Run `reek PATH_TO_FILE_WITH_YOUR_CHANGES` and see if it gives you any good advice. You don't have to do what it says, just consider it.
494
+ * Run specs to make sure you've not broken anything. If it doesn't pass all the specs it doesn't get in.
495
+ - Have a look at coverage/index.htm and see if all your code was checked. We're trying for 100% code coverage.
496
+ * Run `bin/rake docs` to generate documentation.
497
+ - Open up docs/index.html and check your documentation has been added and is clear.
498
+ * Add a short summary of your changes to the CHANGES file. Add your name and a link to your bio/website if you like too.
499
+ * Send me a pull request.
500
+ - Don't merge into the develop branch!
501
+ - Don't merge into the master branch!
502
+ - see [http://nvie.com/posts/a-successful-git-branching-model/](http://nvie.com/posts/a-successful-git-branching-model/) for more on how this is supposed to work.
503
+ * Wait for worldwide fame.
504
+ * Shrug and get on with your life when it doesn't arrive, but know you helped quite a few people in their life, even in a small way - 1000 raindrops will fill a bucket!
505
+
506
+
401
507
  ## Licence ##
402
508
 
403
509
  This is under the MIT Licence.
404
510
 
405
- Copyright (c) 2011 Iain Barnett
511
+ Copyright (c) 2012 Iain Barnett
406
512
 
407
513
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
408
514