redhead 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://www.rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ (MIT License)
2
+
3
+ Copyright (c) 2010 Adam Prescott
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,445 @@
1
+ # What is it?
2
+
3
+ Redhead is for header metadata in strings, in the style of HTTP and email. It makes just a few assumptions about your header names and values, keeps them all as strings, and leaves the rest to you.
4
+
5
+ # How do I use this thing?
6
+
7
+ ## Basics
8
+
9
+ A variable `post` referencing a simple string can be wrapped up in a `Redhead::String` object.
10
+
11
+ >> puts post
12
+ Title: Redhead is for headers!
13
+ Tags: redhead, ruby, headers
14
+
15
+ Since time immemorial, ...
16
+
17
+ >> post = Redhead::String[post]
18
+ => +"Since time immemorial, ..."
19
+
20
+ >> post.headers
21
+ => { { :title => "Redhead is for headers" }, { :tags => "redhead, ruby, headers" } }
22
+
23
+ >> post
24
+ => +"Since time immemorial, ..."
25
+
26
+ >> post.to_s
27
+ => "Since time immemorial, ..."
28
+
29
+ (Note that the `:tags` header has a string value, not an array!)
30
+
31
+ A Redhead::String is prefixed with `+` when inspecting it, so as to distinguish it from a proper `String` instance.
32
+
33
+ ## Regular string functionality
34
+
35
+ A Redhead string has the functionality of a regular Ruby string.
36
+
37
+ >> post.split(/,/).first.reverse
38
+ => "lairomemmi emit ecniS"
39
+
40
+ >> post.split(/ /).first
41
+ => "Since"
42
+
43
+ # Modifies the receiver!
44
+ >> post.reverse!
45
+ => "... ,lairomemmi emit ecniS"
46
+
47
+ >> post.to_s
48
+ => "..., lairomemmi emit ecniS"
49
+
50
+ >> post.headers
51
+ => { { :title => "Redhead is for headers" }, { :tags => "redhead, ruby, headers" } }
52
+
53
+ >> post.replace("Better content.")
54
+ => "Better content."
55
+
56
+ >> post.to_s
57
+ => "Better content."
58
+
59
+ >> post.headers
60
+ => { { :title => "Redhead is for headers" }, { :tags => "redhead, ruby, headers" } }
61
+
62
+ Note that `String` instance methods which are not receiver-modifying will return proper `String` instances, and so headers will be lost.
63
+
64
+ ## Accessing
65
+
66
+ In addition, you get access to headers.
67
+
68
+ >> post.headers[:title]
69
+ => { :title => "Redhead is for headers!" }
70
+
71
+ >> post.headers[:tags].to_s
72
+ => "redhead, ruby, headers"
73
+
74
+ >> post.headers.to_s
75
+ => "Title: Redhead is for headers!\nTags: redhead, ruby, headers"
76
+
77
+ ## Changing values
78
+
79
+ Modifying a header value is easy.
80
+
81
+ >> post.headers[:title] = "A change of title."
82
+ => "A change of title."
83
+
84
+ >> post.headers[:title]
85
+ => { :title => "A change of title." }
86
+
87
+ And changes will carry through:
88
+
89
+ >> post.headers.to_s
90
+ => "Title: A change of title.\nTags: redhead, ruby, headers"
91
+
92
+ ## Objects themselves
93
+
94
+ Alternatively, you can work with the header name-value object itself.
95
+
96
+ >> title_header = post.headers[:title]
97
+ => { :title => "A change of title." }
98
+
99
+ >> title_header.value = "A better title."
100
+ => "A better title."
101
+
102
+ >> title_header
103
+ => { :title => "A better title." }
104
+
105
+ >> title_header.to_s
106
+ => "Title: A better title."
107
+
108
+ ## Adding
109
+
110
+ You can also create and add new headers, in a similar way to modifying an existing header.
111
+
112
+ >> post.headers[:awesome] = "very"
113
+ => "very"
114
+
115
+ >> post.headers
116
+ => { { :title => "A better title." }, { :tags => "redhead, ruby, headers" }, { :awesome => "very" } }
117
+
118
+ >> post.headers.to_s
119
+ => "Title: A better title.\nTags: redhead, ruby, headers\nAwesome: very"
120
+
121
+ Since Ruby assignments always return the right-hand side, there is an alternative syntax which will return the created header.
122
+
123
+ >> post.headers.add(:amount_of_awesome, "high")
124
+ => { :amount_of_awesome => "high" }
125
+
126
+ >> post.headers[:amount_of_awesome].to_s
127
+ => "Amount-Of-Awesome: high"
128
+
129
+ ## Deleting
130
+
131
+ Deleting headers is just as easy.
132
+
133
+ >> post.headers
134
+ => { { :title => "A better title." }, { :tags => "redhead, ruby, headers" }, { :awesome => "very" }, { :amount_of_awesome => "high" } }
135
+
136
+ >> post.headers.delete(:amount_of_awesome)
137
+ => { :amount_of_awesome => "high" }
138
+
139
+ >> post.headers
140
+ => { { :title => "A better title." }, { :tags => "redhead, ruby, headers" }, { :awesome => "very" } }
141
+
142
+ # Finer points
143
+
144
+ There are conventions followed in creating a Redhead string and modifying certain values.
145
+
146
+ ## Names and `:symbols`
147
+
148
+ By default, newly added header field names (i.e., the keys) will become capitalised and hyphen-separated to create their raw header name, so that the symbolic header name `:if_modified_since` becomes the raw header name `If-Modified-Since`. A similar process also happens when turning a string into a Redhead string (in reverse), and when using the default behaviour of `to_s`. To keep symbol names pleasant, by default, anything which isn't `A-Z`, `a-z` or `_` is converted into a `_` character. So `"Some! Long! Header! Name!"` becomes `:some_long_header_name` for accessing within Ruby.
149
+
150
+ For information on changing the formatting rules, see the section on <a href="#special_circumstances">special circumstances</a>.
151
+
152
+ ## Header name memory
153
+
154
+ Original header names are remembered from the input string, and not simply created on-the-fly when using `to_s`. This is to make sure you get the same raw heading name, as a string, that you originally read in, instead of assuming you want it to be changed. Access as a Ruby object, however, is with a symbol by default.
155
+
156
+ If the original string is:
157
+
158
+ WaCKY fieLd NaME: value
159
+
160
+ String's content.
161
+
162
+ Then the header will be turned into a symbol:
163
+
164
+ >> str.headers
165
+ => { { :wacky_field_name => "value" } }
166
+
167
+ But `to_s` will give the original:
168
+
169
+ >> str.headers.to_s
170
+ => "WaCKY fieLd NaME: value"
171
+
172
+ If this had been dynamically produced, it would return `Wacky-Field-Name` by default.
173
+
174
+ If you'd prefer to just let Redhead give you a header name it produces, based off the symbolic header name, use `to_s!`:
175
+
176
+ >> str.headers.to_s!
177
+ => "Wacky-Field-Name: value"
178
+
179
+ For more on this, see below.
180
+
181
+ <h2 id="special_circumstances">Special circumstances</h2>
182
+
183
+ While the default conventions should suit you, you may need to break them. This is for you.
184
+
185
+ ## Caveats
186
+
187
+ Redhead has two main conversions which take place, related to header names. One is to convert a raw header name to what is by default a symbol, the other is to convert from the symbol back to the string, where necessary, for example when using `to_s!`. There may be unexpected behaviour if the symbolic header name does not convert to a raw header name, and back again, i.e., in pseudocode, if `to_symbolic_header_name(to_raw_header_name(some_header.key)) != some_header.key`.
188
+
189
+ >> str.headers
190
+ => { { :awesome_rating => "quite" } }
191
+
192
+ >> output = str.headers.to_s(:awesome_rating => "Something-Completely-Different") + "\n\n" + str.to_s
193
+ => "Something-Completely-Different: quite\n\nString's content."
194
+
195
+ >> input = Redhead::String[output]
196
+ => "String's content."
197
+
198
+ >> input.headers
199
+ => { { :something_completely_different => "quite" } }
200
+
201
+ >> input.headers[:awesome_rating]
202
+ => nil
203
+
204
+ (See below for this use of `to_s`.)
205
+
206
+ There will, however, eventually be a situation where the raw header name needs a better symbolic reference name, or vice versa.
207
+
208
+ With the above caveats in mind, to actually change the raw header name, you can work with the header itself.
209
+
210
+ >> str.headers[:awesome] = "quite"
211
+ => "quite"
212
+
213
+ >> awesome_header = str.headers[:awesome]
214
+ => { :awesome => "quite" }
215
+
216
+ >> awesome_header.raw = "Some-Awe"
217
+ => "Some-Awe"
218
+
219
+ >> awesome_header
220
+ => { :awesome => "quite" }
221
+
222
+ >> awesome_header.to_s
223
+ => "Some-Awe: quite"
224
+
225
+ >> str.headers.to_s
226
+ => "Some-Awe: quite"
227
+
228
+ You can also change the symbolic header name in the same fashion.
229
+
230
+ # Delete to forget about the above
231
+ >> str.headers.delete(:awesome)
232
+ => { :awesome => "quite" }
233
+
234
+ >> str.headers[:awesome] = "quite"
235
+ => "quite"
236
+
237
+ >> awesome_header = str.headers[:awesome]
238
+ => { :awesome => "quite" }
239
+
240
+ >> awesome_header.key = :different_kind_of_awesome
241
+ => :different_kind_of_awesome
242
+
243
+ >> awesome_header
244
+ => { :different_kind_of_awesome => "quite" }
245
+
246
+ >> awesome_header.to_s
247
+ => "Awesome: quite"
248
+
249
+ The original symbolic header name will no longer work.
250
+
251
+ >> str.headers[:awesome]
252
+ => nil
253
+
254
+ >> str.headers[:different_kind_of_awesome].key = :awesome
255
+ => :awesome
256
+
257
+ >> awesome_header
258
+ => { :awesome => "quite" }
259
+
260
+ >> awesome_header.to_s
261
+ => "Awesome: quite"
262
+
263
+ >> str.headers[:different_kind_of_awesome]
264
+ => nil
265
+
266
+ As a further option, there is `headers!`, which allows more one-step control, working with a hash argument. All _changed_ headers are returned.
267
+
268
+ >> str.headers
269
+ => { { :awesome => "quite" } }
270
+
271
+ >> str.headers[:temp] = "temp"
272
+ => "temp"
273
+
274
+ >> str.headers
275
+ => { { :awesome => "quite" }, { :temp => "temp" } }
276
+
277
+ >> str.headers.to_s
278
+ => "Some-Awe: quite\nTemp: temp"
279
+
280
+ >> str.headers!(:awesome => { :key => :awesome_rating, :raw => "Awesome-Rating" })
281
+ => { { :awesome_rating => "quite" } }
282
+
283
+ >> str.headers
284
+ => { { :awesome => "quite" }, { :temp => "temp" } }
285
+
286
+ Omitting one of `:raw` and `:key` will work as you expect.
287
+
288
+ ## Non-destructive raw header changes
289
+
290
+ To work with a different raw header name, without modifying anything, you can pass a hash to `to_s`. This does not leave a side-effect and is only temporary.
291
+
292
+ >> str.headers
293
+ => { { :awesome => "quite" }, { :temp => "temp" } }
294
+
295
+ >> str.headers.to_s
296
+ => "Awesome-Rating: quite\nTemp: temp"
297
+
298
+ >> str.headers.to_s(:awesome => "Something-To-Do with Awesome-ness", :temp => "A very TEMPORARY header name")
299
+ => "Something-To-Do with Awesome-ness: quite\nA very TEMPORARY header name"
300
+
301
+ # Nothing changed.
302
+ >> str.headers
303
+ => { { :awesome => "quite" }, { :temp => "temp" } }
304
+
305
+ >> str.headers.to_s
306
+ => "Awesome-Rating: quite\nTemp: temp"
307
+
308
+ ## Mismatching at creation
309
+
310
+ The custom raw header name can also be given explicitly at creation time.
311
+
312
+ >> str.headers
313
+ => { { :awesome => "quite" }, { :temp => "temp" } }
314
+
315
+ >> str.headers.delete(:temp)
316
+ => { :temp => "temp" }
317
+
318
+ >> str.headers
319
+ => { { :awesome => "quite" } }
320
+
321
+ >> str.heders.add(:temporary, "temp", "A-Rather-Temporary-Value")
322
+ => { :temp => "temp" }
323
+
324
+ >> str.headers.to_s
325
+ => "Awesome-Rating: quite\nA-Rather-Temporary-Value: temp"
326
+
327
+ # Enterprise Header Solutions
328
+
329
+ As mentioned, Redhead performs two conversions. One to produce a symbolic header name from the raw header name, and one to produce the raw header name from the symbolic header name. The symbolic -> raw process is used in `to_s!`, to force header names to be produced instead of using the header name remembered from when the header was created.
330
+
331
+ If you need to control the format of header names beyond the simple separator option given above, you can provide a block, the result of which is the name used for the raw or symbolic header name.
332
+
333
+ If that doesn't make a lot of sense on the first read, don't worry, here's some code.
334
+
335
+ >> string = "A-Header-Name: a header value\n\nContent."
336
+ => "A-Header-Name: a header value\n\nContent."
337
+
338
+ >> str = Redhead::String.new(string) do |name|
339
+ ?> name.split(/-/).join("").upcase.to_sym
340
+ ?> end
341
+ => +"Content."
342
+
343
+ >> str.headers
344
+ => { { :AHEADERNAME => "a header value" } }
345
+
346
+ Note that this uses `Redhead::String.new` instead of `Redhead::String.[]` because of the block argument.
347
+
348
+ The above defines how symbolic headers are created in when creating the header objects. Using this approach, you can work with non-standard headers quite easily.
349
+
350
+ Note that `to_sym` is _not_ implicit, to suggest you use a pleasant symbol as the key.
351
+
352
+ It's also possible to specify the code to be used when calling `to_s`:
353
+
354
+ >> str.headers
355
+ => { { :AHEADERNAME => "a header value" } }
356
+
357
+ >> str.headers.to_s do |name|
358
+ ?> name.to_s.downcase.scan(/..?.?/).join("-").capitalize
359
+ ?> end
360
+ => "ahe-ade-rna-me: a header value"
361
+
362
+ The block to `to_s` will not modify the headers in-place, in keeping with the behaviour of the block-less `to_s`. To change how the symbolic-to-raw header name conversion works, you can do so on the object holding the headers.
363
+
364
+ >> str.headers.to_raw = lambda do |name|
365
+ ?> name.to_s.downcase.scan(/..?.?/).join("-").capitalize
366
+ ?> end
367
+ => #<Proc:...>
368
+
369
+ Similarly, you can modify `to_key`. You can also change `to_raw` and `to_key` for each individual header. If no block is given for a specific header, it defaults to the block for the containing `headers` object. If nothing is given _there_, then it goes to the default.
370
+
371
+ If `to_raw(produced_key) != original_key` for all the headers in the object, then the headers are in a mismatched state. Equally, a single header is in a mismatched state if the condition fails for that header.
372
+
373
+ This can be checked with `reversible?`.
374
+
375
+ >> string = "A-Header-Name: a header value\n\nContent."
376
+ => "A-Header-Name: a header value\n\nContent."
377
+
378
+ >> str = Redhead::String.new(string) do |name|
379
+ ?> name.gsub(/-/, "").upcase.to_sym
380
+ ?> end
381
+ => +"Content."
382
+
383
+ # At this point, `to_key` is not reversible via `to_raw`
384
+
385
+ >> str.headers.reversible?
386
+ => false
387
+
388
+ >> str = Redhead::String.new(string) do |name|
389
+ ?> name.split(/-/).map { |e| e.upcase }.join("zzz").to_sym
390
+ ?> end
391
+ => +"Content."
392
+
393
+ >> str.headers
394
+ => { { :AzzzHEADERzzzNAME => "a header value" } }
395
+
396
+ >> str.headers.reversible?
397
+ => false
398
+
399
+ >> str.headers.to_raw = lambda do |name|
400
+ ?> name.to_s.split(/zzz/).map { |e| e.capitalize }.join("-")
401
+ ?> end
402
+ => #<Proc:...>
403
+
404
+ # We can go back and forth without issue on this string
405
+
406
+ >> str.headers.reversible?
407
+ => true
408
+
409
+ >> str.headers
410
+ => { { :AzzzHEADERzzzzNAME => "a header value" } }
411
+
412
+ >> str.headers.to_s
413
+ => "A-Header-Name: a header value"
414
+
415
+ Reversibility is checked by calling `reversible?` on all the headers in `str.headers`, since each header can have its own `to_key` and `to_raw` blocks. `reversible?` returning false will not raise an error or stop execution.
416
+
417
+ When creating new headers, `to_raw`, is used, meaning your custom block will be picked up and used to create the raw header as though it had been created from a string.
418
+
419
+ >> str.headers.to_raw = proc { "Genuinely" }
420
+
421
+ >> str.headers[:foo_bar] = "temp"
422
+ => "temp"
423
+
424
+ >> temp_header = str.headers[:foo_bar]
425
+ => { :foo_bar => "temp" }
426
+
427
+ >> temp_header.to_s
428
+ => "Genuinely: temp"
429
+
430
+ Changing `to_raw` after-the-fact will not change the raw header name stored for the object. To force `to_raw` to be used instead of the stored value, use `to_s!`, which _always_ uses `to_raw`.
431
+
432
+ >> temp_header.to_raw = lambda { "nothing meaningful" }
433
+ => #<Proc:...>
434
+
435
+ >> temp_header.to_s
436
+ => "Temp-Orary-Header: temp"
437
+
438
+ >> temp_header.to_s!
439
+ => "nothing meaningful: temp"
440
+
441
+ # TODO
442
+
443
+ Headers on different lines with the same raw name. Important for HTTP.
444
+
445
+ Improve docs.