lightningdb-happymapper 0.3.0

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.
@@ -0,0 +1,555 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+ require 'pp'
3
+ require 'uri'
4
+
5
+ class Address
6
+ include HappyMapper
7
+
8
+ tag 'address'
9
+ element :street, String
10
+ element :postcode, String
11
+ element :housenumber, String
12
+ element :city, String
13
+ element :country, String
14
+ end
15
+
16
+ class Feature
17
+ include HappyMapper
18
+ element :name, String, :tag => '.|.//text()'
19
+ end
20
+
21
+ class FeatureBullet
22
+ include HappyMapper
23
+
24
+ tag 'features_bullets'
25
+ has_many :features, Feature
26
+ element :bug, String
27
+ end
28
+
29
+ class Product
30
+ include HappyMapper
31
+
32
+ element :title, String
33
+ has_one :feature_bullets, FeatureBullet
34
+ has_one :address, Address
35
+ end
36
+
37
+ module FamilySearch
38
+ class Person
39
+ include HappyMapper
40
+
41
+ attribute :version, String
42
+ attribute :modified, Time
43
+ attribute :id, String
44
+ end
45
+
46
+ class Persons
47
+ include HappyMapper
48
+ has_many :person, Person
49
+ end
50
+
51
+ class FamilyTree
52
+ include HappyMapper
53
+
54
+ tag 'familytree'
55
+ attribute :version, String
56
+ attribute :status_message, String, :tag => 'statusMessage'
57
+ attribute :status_code, String, :tag => 'statusCode'
58
+ has_one :persons, Persons
59
+ end
60
+ end
61
+
62
+ module FedEx
63
+ class Address
64
+ include HappyMapper
65
+
66
+ tag 'Address'
67
+ namespace 'v2'
68
+ element :city, String, :tag => 'City'
69
+ element :state, String, :tag => 'StateOrProvinceCode'
70
+ element :zip, String, :tag => 'PostalCode'
71
+ element :countrycode, String, :tag => 'CountryCode'
72
+ element :residential, Boolean, :tag => 'Residential'
73
+ end
74
+
75
+ class Event
76
+ include HappyMapper
77
+
78
+ tag 'Events'
79
+ namespace 'v2'
80
+ element :timestamp, String, :tag => 'Timestamp'
81
+ element :eventtype, String, :tag => 'EventType'
82
+ element :eventdescription, String, :tag => 'EventDescription'
83
+ has_one :address, Address
84
+ end
85
+
86
+ class PackageWeight
87
+ include HappyMapper
88
+
89
+ tag 'PackageWeight'
90
+ namespace 'v2'
91
+ element :units, String, :tag => 'Units'
92
+ element :value, Integer, :tag => 'Value'
93
+ end
94
+
95
+ class TrackDetails
96
+ include HappyMapper
97
+
98
+ tag 'TrackDetails'
99
+ namespace 'v2'
100
+ element :tracking_number, String, :tag => 'TrackingNumber'
101
+ element :status_code, String, :tag => 'StatusCode'
102
+ element :status_desc, String, :tag => 'StatusDescription'
103
+ element :carrier_code, String, :tag => 'CarrierCode'
104
+ element :service_info, String, :tag => 'ServiceInfo'
105
+ has_one :weight, PackageWeight, :tag => 'PackageWeight'
106
+ element :est_delivery, String, :tag => 'EstimatedDeliveryTimestamp'
107
+ has_many :events, Event
108
+ end
109
+
110
+ class Notification
111
+ include HappyMapper
112
+
113
+ tag 'Notifications'
114
+ namespace 'v2'
115
+ element :severity, String, :tag => 'Severity'
116
+ element :source, String, :tag => 'Source'
117
+ element :code, Integer, :tag => 'Code'
118
+ element :message, String, :tag => 'Message'
119
+ element :localized_message, String, :tag => 'LocalizedMessage'
120
+ end
121
+
122
+ class TransactionDetail
123
+ include HappyMapper
124
+
125
+ tag 'TransactionDetail'
126
+ namespace 'v2'
127
+ element :cust_tran_id, String, :tag => 'CustomerTransactionId'
128
+ end
129
+
130
+ class TrackReply
131
+ include HappyMapper
132
+
133
+ tag 'TrackReply'
134
+ namespace 'v2'
135
+ element :highest_severity, String, :tag => 'HighestSeverity'
136
+ element :more_data, Boolean, :tag => 'MoreData'
137
+ has_many :notifications, Notification, :tag => 'Notifications'
138
+ has_many :trackdetails, TrackDetails, :tag => 'TrackDetails'
139
+ has_one :tran_detail, TransactionDetail, :tab => 'TransactionDetail'
140
+ end
141
+ end
142
+
143
+ class Place
144
+ include HappyMapper
145
+ element :name, String
146
+ end
147
+
148
+ class Radar
149
+ include HappyMapper
150
+ has_many :places, Place, :tag => :place
151
+ end
152
+
153
+ class Post
154
+ include HappyMapper
155
+
156
+ attribute :href, String
157
+ attribute :hash, String
158
+ attribute :description, String
159
+ attribute :tag, String
160
+ attribute :time, Time
161
+ attribute :others, Integer
162
+ attribute :extended, String
163
+ end
164
+
165
+ class User
166
+ include HappyMapper
167
+
168
+ element :id, Integer
169
+ element :name, String
170
+ element :screen_name, String
171
+ element :location, String
172
+ element :description, String
173
+ element :profile_image_url, String
174
+ element :url, String
175
+ element :protected, Boolean
176
+ element :followers_count, Integer
177
+ end
178
+
179
+ class Status
180
+ include HappyMapper
181
+
182
+ element :id, Integer
183
+ element :text, String
184
+ element :created_at, Time
185
+ element :source, String
186
+ element :truncated, Boolean
187
+ element :in_reply_to_status_id, Integer
188
+ element :in_reply_to_user_id, Integer
189
+ element :favorited, Boolean
190
+ element :non_existent, String, :tag => 'dummy', :namespace => 'fake'
191
+ has_one :user, User
192
+ end
193
+
194
+ class CurrentWeather
195
+ include HappyMapper
196
+
197
+ tag 'ob'
198
+ namespace 'aws'
199
+ element :temperature, Integer, :tag => 'temp'
200
+ element :feels_like, Integer, :tag => 'feels-like'
201
+ element :current_condition, String, :tag => 'current-condition', :attributes => {:icon => String}
202
+ end
203
+
204
+ # for type coercion
205
+ class ProductGroup < String; end
206
+
207
+ module PITA
208
+ class Item
209
+ include HappyMapper
210
+
211
+ tag 'Item' # if you put class in module you need tag
212
+ element :asin, String, :tag => 'ASIN'
213
+ element :detail_page_url, URI, :tag => 'DetailPageURL', :parser => :parse
214
+ element :manufacturer, String, :tag => 'Manufacturer', :deep => true
215
+ element :point, String, :tag => 'point', :namespace => 'georss'
216
+ element :product_group, ProductGroup, :tag => 'ProductGroup', :deep => true, :parser => :new, :raw => true
217
+ end
218
+
219
+ class Items
220
+ include HappyMapper
221
+
222
+ tag 'Items' # if you put class in module you need tag
223
+ element :total_results, Integer, :tag => 'TotalResults'
224
+ element :total_pages, Integer, :tag => 'TotalPages'
225
+ has_many :items, Item
226
+ end
227
+ end
228
+
229
+ module GitHub
230
+ class Commit
231
+ include HappyMapper
232
+
233
+ tag "commit"
234
+ element :url, String
235
+ element :tree, String
236
+ element :message, String
237
+ element :id, String
238
+ element :'committed-date', Date
239
+ end
240
+ end
241
+
242
+ module QuarterTest
243
+ class Quarter
244
+ include HappyMapper
245
+
246
+ element :start, String
247
+ end
248
+
249
+ class Details
250
+ include HappyMapper
251
+
252
+ element :round, Integer
253
+ element :quarter, Integer
254
+ end
255
+
256
+ class Game
257
+ include HappyMapper
258
+
259
+ # in an ideal world, the following elements would all be
260
+ # called 'quarter' with an attribute indicating which quarter
261
+ # it represented, but the refactoring that allows a single class
262
+ # to be used for all these differently named elements is the next
263
+ # best thing
264
+ has_one :details, QuarterTest::Details
265
+ has_one :q1, QuarterTest::Quarter, :tag => 'q1'
266
+ has_one :q2, QuarterTest::Quarter, :tag => 'q2'
267
+ has_one :q3, QuarterTest::Quarter, :tag => 'q3'
268
+ has_one :q4, QuarterTest::Quarter, :tag => 'q4'
269
+ end
270
+ end
271
+
272
+ describe HappyMapper do
273
+
274
+ describe "being included into another class" do
275
+ before do
276
+ Foo.instance_variable_set("@attributes", {})
277
+ Foo.instance_variable_set("@elements", {})
278
+ end
279
+ class Foo; include HappyMapper end
280
+
281
+ it "should set attributes to an array" do
282
+ Foo.attributes.should == []
283
+ end
284
+
285
+ it "should set @elements to a hash" do
286
+ Foo.elements.should == []
287
+ end
288
+
289
+ it "should allow adding an attribute" do
290
+ lambda {
291
+ Foo.attribute :name, String
292
+ }.should change(Foo, :attributes)
293
+ end
294
+
295
+ it "should allow adding an attribute containing a dash" do
296
+ lambda {
297
+ Foo.attribute :'bar-baz', String
298
+ }.should change(Foo, :attributes)
299
+ end
300
+
301
+ it "should be able to get all attributes in array" do
302
+ Foo.attribute :name, String
303
+ Foo.attributes.size.should == 1
304
+ end
305
+
306
+ it "should allow adding an element" do
307
+ lambda {
308
+ Foo.element :name, String
309
+ }.should change(Foo, :elements)
310
+ end
311
+
312
+ it "should allow adding an element containing a dash" do
313
+ lambda {
314
+ Foo.element :'bar-baz', String
315
+ }.should change(Foo, :elements)
316
+
317
+ end
318
+
319
+ it "should be able to get all elements in array" do
320
+ Foo.element(:name, String)
321
+ Foo.elements.size.should == 1
322
+ end
323
+
324
+ it "should allow has one association" do
325
+ Foo.has_one(:user, User)
326
+ element = Foo.elements.first
327
+ element.name.should == 'user'
328
+ element.type.should == User
329
+ element.options[:single] = true
330
+ end
331
+
332
+ it "should allow has many association" do
333
+ Foo.has_many(:users, User)
334
+ element = Foo.elements.first
335
+ element.name.should == 'users'
336
+ element.type.should == User
337
+ element.options[:single] = false
338
+ end
339
+
340
+ it "should default tag name to lowercase class" do
341
+ Foo.tag_name.should == 'foo'
342
+ end
343
+
344
+ it "should default tag name of class in modules to the last constant lowercase" do
345
+ module Bar; class Baz; include HappyMapper; end; end
346
+ Bar::Baz.tag_name.should == 'baz'
347
+ end
348
+
349
+ it "should allow setting tag name" do
350
+ Foo.tag('FooBar')
351
+ Foo.tag_name.should == 'FooBar'
352
+ end
353
+
354
+ it "should allow setting a namespace" do
355
+ Foo.namespace(namespace = "foo")
356
+ Foo.namespace.should == namespace
357
+ end
358
+
359
+ it "should provide #parse" do
360
+ Foo.should respond_to(:parse)
361
+ end
362
+ end
363
+
364
+ describe "#attributes" do
365
+ it "should only return attributes for the current class" do
366
+ Post.attributes.size.should == 7
367
+ Status.attributes.size.should == 0
368
+ end
369
+ end
370
+
371
+ describe "#elements" do
372
+ it "should only return elements for the current class" do
373
+ Post.elements.size.should == 0
374
+ Status.elements.size.should == 10
375
+ end
376
+ end
377
+
378
+ it "should parse xml attributes into ruby objects" do
379
+ posts = Post.parse(fixture_file('posts.xml'))
380
+ posts.size.should == 20
381
+ first = posts.first
382
+ first.href.should == 'http://roxml.rubyforge.org/'
383
+ first.hash.should == '19bba2ab667be03a19f67fb67dc56917'
384
+ first.description.should == 'ROXML - Ruby Object to XML Mapping Library'
385
+ first.tag.should == 'ruby xml gems mapping'
386
+ first.time.should == Time.utc(2008, 8, 9, 5, 24, 20)
387
+ first.others.should == 56
388
+ first.extended.should == 'ROXML is a Ruby library designed to make it easier for Ruby developers to work with XML. Using simple annotations, it enables Ruby classes to be custom-mapped to XML. ROXML takes care of the marshalling and unmarshalling of mapped attributes so that developers can focus on building first-class Ruby classes.'
389
+ end
390
+
391
+ it "should parse xml elements to ruby objcts" do
392
+ statuses = Status.parse(fixture_file('statuses.xml'))
393
+ statuses.size.should == 20
394
+ first = statuses.first
395
+ first.id.should == 882281424
396
+ first.created_at.should == Time.utc(2008, 8, 9, 5, 38, 12)
397
+ first.source.should == 'web'
398
+ first.truncated.should be_false
399
+ first.in_reply_to_status_id.should == 1234
400
+ first.in_reply_to_user_id.should == 12345
401
+ first.favorited.should be_false
402
+ first.user.id.should == 4243
403
+ first.user.name.should == 'John Nunemaker'
404
+ first.user.screen_name.should == 'jnunemaker'
405
+ first.user.location.should == 'Mishawaka, IN, US'
406
+ first.user.description.should == 'Loves his wife, ruby, notre dame football and iu basketball'
407
+ first.user.profile_image_url.should == 'http://s3.amazonaws.com/twitter_production/profile_images/53781608/Photo_75_normal.jpg'
408
+ first.user.url.should == 'http://addictedtonew.com'
409
+ first.user.protected.should be_false
410
+ first.user.followers_count.should == 486
411
+ end
412
+
413
+ it "should parse xml containing the desired element as root node" do
414
+ address = Address.parse(fixture_file('address.xml'), :single => true)
415
+ address.street.should == 'Milchstrasse'
416
+ address.postcode.should == '26131'
417
+ address.housenumber.should == '23'
418
+ address.city.should == 'Oldenburg'
419
+ address.country.should == 'Germany'
420
+ end
421
+
422
+ it "should parse xml with default namespace (amazon)" do
423
+ file_contents = fixture_file('pita.xml')
424
+ items = PITA::Items.parse(file_contents, :single => true)
425
+ items.total_results.should == 22
426
+ items.total_pages.should == 3
427
+ first = items.items[0]
428
+ second = items.items[1]
429
+ first.asin.should == '0321480791'
430
+ first.point.should == '38.5351715088 -121.7948684692'
431
+ first.detail_page_url.should be_a_kind_of(URI)
432
+ first.detail_page_url.to_s.should == 'http://www.amazon.com/gp/redirect.html%3FASIN=0321480791%26tag=ws%26lcode=xm2%26cID=2025%26ccmID=165953%26location=/o/ASIN/0321480791%253FSubscriptionId=dontbeaswoosh'
433
+ first.manufacturer.should == 'Addison-Wesley Professional'
434
+ first.product_group.should == '<ProductGroup>Book</ProductGroup>'
435
+ second.asin.should == '047022388X'
436
+ second.manufacturer.should == 'Wrox'
437
+ end
438
+
439
+ it "should parse xml that has attributes of elements" do
440
+ items = CurrentWeather.parse(fixture_file('current_weather.xml'))
441
+ first = items[0]
442
+ first.temperature.should == 51
443
+ first.feels_like.should == 51
444
+ first.current_condition.should == 'Sunny'
445
+ first.current_condition.icon.should == 'http://deskwx.weatherbug.com/images/Forecast/icons/cond007.gif'
446
+ end
447
+
448
+ it "should parse xml with nested elements" do
449
+ radars = Radar.parse(fixture_file('radar.xml'))
450
+ first = radars[0]
451
+ first.places.size.should == 1
452
+ first.places[0].name.should == 'Store'
453
+ second = radars[1]
454
+ second.places.size.should == 0
455
+ third = radars[2]
456
+ third.places.size.should == 2
457
+ third.places[0].name.should == 'Work'
458
+ third.places[1].name.should == 'Home'
459
+ end
460
+
461
+ it "should parse xml with element name different to class name" do
462
+ game = QuarterTest::Game.parse(fixture_file('quarters.xml'))
463
+ game.q1.start.should == '4:40:15 PM'
464
+ game.q2.start.should == '5:18:53 PM'
465
+ end
466
+
467
+ it "should parse xml that has elements with dashes" do
468
+ commit = GitHub::Commit.parse(fixture_file('commit.xml'))
469
+ commit.message.should == "move commands.rb and helpers.rb into commands/ dir"
470
+ commit.url.should == "http://github.com/defunkt/github-gem/commit/c26d4ce9807ecf57d3f9eefe19ae64e75bcaaa8b"
471
+ commit.id.should == "c26d4ce9807ecf57d3f9eefe19ae64e75bcaaa8b"
472
+ commit.committed_date.should == Date.parse("2008-03-02T16:45:41-08:00")
473
+ commit.tree.should == "28a1a1ca3e663d35ba8bf07d3f1781af71359b76"
474
+ end
475
+
476
+ it "should parse xml with no namespace" do
477
+ product = Product.parse(fixture_file('product_no_namespace.xml'), :single => true)
478
+ product.title.should == "A Title"
479
+ product.feature_bullets.bug.should == 'This is a bug'
480
+ product.feature_bullets.features.size.should == 2
481
+ product.feature_bullets.features[0].name.should == 'This is feature text 1'
482
+ product.feature_bullets.features[1].name.should == 'This is feature text 2'
483
+ end
484
+
485
+ it "should parse xml with default namespace" do
486
+ product = Product.parse(fixture_file('product_default_namespace.xml'), :single => true)
487
+ product.title.should == "A Title"
488
+ product.feature_bullets.bug.should == 'This is a bug'
489
+ product.feature_bullets.features.size.should == 2
490
+ product.feature_bullets.features[0].name.should == 'This is feature text 1'
491
+ product.feature_bullets.features[1].name.should == 'This is feature text 2'
492
+ end
493
+
494
+ it "should parse xml with single namespace" do
495
+ product = Product.parse(fixture_file('product_single_namespace.xml'), :single => true)
496
+ product.title.should == "A Title"
497
+ product.feature_bullets.bug.should == 'This is a bug'
498
+ product.feature_bullets.features.size.should == 2
499
+ product.feature_bullets.features[0].name.should == 'This is feature text 1'
500
+ product.feature_bullets.features[1].name.should == 'This is feature text 2'
501
+ end
502
+
503
+ it "should parse xml with multiple namespaces" do
504
+ track = FedEx::TrackReply.parse(fixture_file('multiple_namespaces.xml'))
505
+ track.highest_severity.should == 'SUCCESS'
506
+ track.more_data.should be_false
507
+ notification = track.notifications.first
508
+ notification.code.should == 0
509
+ notification.localized_message.should == 'Request was successfully processed.'
510
+ notification.message.should == 'Request was successfully processed.'
511
+ notification.severity.should == 'SUCCESS'
512
+ notification.source.should == 'trck'
513
+ detail = track.trackdetails.first
514
+ detail.carrier_code.should == 'FDXG'
515
+ detail.est_delivery.should == '2009-01-02T00:00:00'
516
+ detail.service_info.should == 'Ground-Package Returns Program-Domestic'
517
+ detail.status_code.should == 'OD'
518
+ detail.status_desc.should == 'On FedEx vehicle for delivery'
519
+ detail.tracking_number.should == '9611018034267800045212'
520
+ detail.weight.units.should == 'LB'
521
+ detail.weight.value.should == 2
522
+ events = detail.events
523
+ events.size.should == 10
524
+ first_event = events[0]
525
+ first_event.eventdescription.should == 'On FedEx vehicle for delivery'
526
+ first_event.eventtype.should == 'OD'
527
+ first_event.timestamp.should == '2009-01-02T06:00:00'
528
+ first_event.address.city.should == 'WICHITA'
529
+ first_event.address.countrycode.should == 'US'
530
+ first_event.address.residential.should be_false
531
+ first_event.address.state.should == 'KS'
532
+ first_event.address.zip.should == '67226'
533
+ last_event = events[-1]
534
+ last_event.eventdescription.should == 'In FedEx possession'
535
+ last_event.eventtype.should == 'IP'
536
+ last_event.timestamp.should == '2008-12-27T09:40:00'
537
+ last_event.address.city.should == 'LONGWOOD'
538
+ last_event.address.countrycode.should == 'US'
539
+ last_event.address.residential.should be_false
540
+ last_event.address.state.should == 'FL'
541
+ last_event.address.zip.should == '327506398'
542
+ track.tran_detail.cust_tran_id.should == '20090102-111321'
543
+ end
544
+
545
+ xit "should parse family search xml" do
546
+ tree = FamilySearch::FamilyTree.parse(fixture_file('family_tree.xml'))
547
+ tree.version.should == '1.0.20071213.942'
548
+ tree.status_message.should == 'OK'
549
+ tree.status_code.should == '200'
550
+ # tree.people.size.should == 1
551
+ # tree.people.first.version.should == '1199378491000'
552
+ # tree.people.first.modified.should == Time.utc(2008, 1, 3, 16, 41, 31) # 2008-01-03T09:41:31-07:00
553
+ # tree.people.first.id.should == 'KWQS-BBQ'
554
+ end
555
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,13 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ gem 'rspec'
6
+ require 'spec'
7
+ end
8
+
9
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'happymapper')
10
+
11
+ def fixture_file(filename)
12
+ File.read(File.dirname(__FILE__) + "/fixtures/#{filename}")
13
+ end
@@ -0,0 +1,47 @@
1
+ @media screen, projection {
2
+ /*
3
+ Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4
+ Code licensed under the BSD License:
5
+ http://developer.yahoo.net/yui/license.txt
6
+ version: 2.2.0
7
+ */
8
+ body {font:13px arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}table {font-size:inherit;font:100%;}select, input, textarea {font:99% arial,helvetica,clean,sans-serif;}pre, code {font:115% monospace;*font-size:100%;}body * {line-height:1.22em;}
9
+ body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}/*ol,ul {list-style:none;}*/caption,th {text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym {border:0;}
10
+ /* end of yahoo reset and fonts */
11
+
12
+ body {color:#333; background:#4b1a1a; line-height:1.3;}
13
+ p {margin:0 0 20px;}
14
+ a {color:#4b1a1a;}
15
+ a:hover {text-decoration:none;}
16
+ strong {font-weight:bold;}
17
+ em {font-style:italics;}
18
+ h1,h2,h3,h4,h5,h6 {font-weight:bold;}
19
+ h1 {font-size:197%; margin:30px 0; color:#4b1a1a;}
20
+ h2 {font-size:174%; margin:20px 0; color:#b8111a;}
21
+ h3 {font-size:152%; margin:10px 0;}
22
+ h4 {font-size:129%; margin:10px 0;}
23
+ pre {background:#eee; margin:0 0 20px; padding:20px; border:1px solid #ccc; font-size:100%; overflow:auto;}
24
+ code {font-size:100%; margin:0; padding:0;}
25
+ ul, ol {margin:10px 0 10px 25px;}
26
+ ol li {margin:0 0 10px;}
27
+
28
+
29
+
30
+
31
+
32
+ div#wrapper {background:#fff; width:560px; margin:0 auto; padding:20px; border:10px solid #bc8c46; border-width:0 10px;}
33
+ div#header {position:relative; border-bottom:1px dotted; margin:0 0 10px; padding:0 0 10px;}
34
+ div#header p {margin:0; padding:0;}
35
+ div#header h1 {margin:0; padding:0;}
36
+ ul#nav {position:absolute; top:0; right:0; list-style:none; margin:0; padding:0;}
37
+ ul#nav li {display:inline; padding:0 0 0 5px;}
38
+ ul#nav li a {}
39
+ div#content {}
40
+ div#footer {margin:40px 0 0; border-top:1px dotted; padding:10px 0 0;}
41
+
42
+
43
+
44
+
45
+
46
+
47
+ }