redhead 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.