stn-dcell 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +7 -0
  4. data/.rspec +4 -0
  5. data/.travis.yml +30 -0
  6. data/CHANGES.md +53 -0
  7. data/Gemfile +18 -0
  8. data/LICENSE.txt +20 -0
  9. data/README.md +168 -0
  10. data/Rakefile +4 -0
  11. data/benchmarks/messaging.rb +73 -0
  12. data/benchmarks/receiver.rb +37 -0
  13. data/dcell.gemspec +29 -0
  14. data/examples/itchy.rb +26 -0
  15. data/examples/scratchy.rb +12 -0
  16. data/explorer/css/bootstrap-responsive.css +686 -0
  17. data/explorer/css/bootstrap-responsive.min.css +12 -0
  18. data/explorer/css/bootstrap.css +3990 -0
  19. data/explorer/css/bootstrap.min.css +689 -0
  20. data/explorer/css/explorer.css +28 -0
  21. data/explorer/ico/favicon.ico +0 -0
  22. data/explorer/img/glyphicons-halflings-white.png +0 -0
  23. data/explorer/img/glyphicons-halflings.png +0 -0
  24. data/explorer/img/logo.png +0 -0
  25. data/explorer/index.html.erb +94 -0
  26. data/explorer/js/bootstrap.js +1726 -0
  27. data/explorer/js/bootstrap.min.js +6 -0
  28. data/lib/dcell.rb +127 -0
  29. data/lib/dcell/actor_proxy.rb +30 -0
  30. data/lib/dcell/celluloid_ext.rb +120 -0
  31. data/lib/dcell/directory.rb +31 -0
  32. data/lib/dcell/explorer.rb +74 -0
  33. data/lib/dcell/future_proxy.rb +32 -0
  34. data/lib/dcell/global.rb +23 -0
  35. data/lib/dcell/info_service.rb +122 -0
  36. data/lib/dcell/mailbox_proxy.rb +53 -0
  37. data/lib/dcell/messages.rb +65 -0
  38. data/lib/dcell/node.rb +138 -0
  39. data/lib/dcell/node_manager.rb +79 -0
  40. data/lib/dcell/registries/cassandra_adapter.rb +126 -0
  41. data/lib/dcell/registries/mongodb_adapter.rb +85 -0
  42. data/lib/dcell/registries/redis_adapter.rb +95 -0
  43. data/lib/dcell/registries/zk_adapter.rb +123 -0
  44. data/lib/dcell/responses.rb +16 -0
  45. data/lib/dcell/router.rb +46 -0
  46. data/lib/dcell/rpc.rb +95 -0
  47. data/lib/dcell/rspec.rb +1 -0
  48. data/lib/dcell/server.rb +73 -0
  49. data/lib/dcell/version.rb +3 -0
  50. data/logo.png +0 -0
  51. data/spec/dcell/actor_proxy_spec.rb +72 -0
  52. data/spec/dcell/celluloid_ext_spec.rb +32 -0
  53. data/spec/dcell/directory_spec.rb +22 -0
  54. data/spec/dcell/explorer_spec.rb +17 -0
  55. data/spec/dcell/global_spec.rb +25 -0
  56. data/spec/dcell/node_spec.rb +23 -0
  57. data/spec/dcell/registries/mongodb_adapter_spec.rb +7 -0
  58. data/spec/dcell/registries/redis_adapter_spec.rb +6 -0
  59. data/spec/dcell/registries/zk_adapter_spec.rb +28 -0
  60. data/spec/options/01-options.rb +10 -0
  61. data/spec/options/02-registry.rb +13 -0
  62. data/spec/spec_helper.rb +28 -0
  63. data/spec/support/helpers.rb +46 -0
  64. data/spec/support/registry_examples.rb +35 -0
  65. data/spec/test_node.rb +38 -0
  66. data/tasks/cassandra.task +84 -0
  67. data/tasks/rspec.task +7 -0
  68. data/tasks/zookeeper.task +58 -0
  69. metadata +239 -0
data/examples/itchy.rb ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+ require 'dcell'
3
+
4
+ DCell.start :id => "itchy"
5
+
6
+ class Itchy
7
+ include Celluloid
8
+
9
+ def initialize
10
+ puts "Ready for mayhem!"
11
+ @n = 0
12
+ end
13
+
14
+ def fight
15
+ @n += 1
16
+ if @n % 6 == 0
17
+ puts "Bite!"
18
+ else
19
+ puts "Fight!"
20
+ end
21
+ @n
22
+ end
23
+ end
24
+
25
+ Itchy.supervise_as :itchy
26
+ sleep
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ require 'dcell'
3
+
4
+ DCell.start
5
+ itchy_node = DCell::Node["itchy"]
6
+
7
+ puts "Fighting itchy! (check itchy's output)"
8
+
9
+ 300.times do
10
+ puts itchy_node[:itchy].fight
11
+ sleep 1
12
+ end
@@ -0,0 +1,686 @@
1
+ /*!
2
+ * Bootstrap Responsive v2.0.2
3
+ *
4
+ * Copyright 2012 Twitter, Inc
5
+ * Licensed under the Apache License v2.0
6
+ * http://www.apache.org/licenses/LICENSE-2.0
7
+ *
8
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
9
+ */
10
+ .clearfix {
11
+ *zoom: 1;
12
+ }
13
+ .clearfix:before,
14
+ .clearfix:after {
15
+ display: table;
16
+ content: "";
17
+ }
18
+ .clearfix:after {
19
+ clear: both;
20
+ }
21
+ .hide-text {
22
+ overflow: hidden;
23
+ text-indent: 100%;
24
+ white-space: nowrap;
25
+ }
26
+ .input-block-level {
27
+ display: block;
28
+ width: 100%;
29
+ min-height: 28px;
30
+ /* Make inputs at least the height of their button counterpart */
31
+
32
+ /* Makes inputs behave like true block-level elements */
33
+
34
+ -webkit-box-sizing: border-box;
35
+ -moz-box-sizing: border-box;
36
+ -ms-box-sizing: border-box;
37
+ box-sizing: border-box;
38
+ }
39
+ .hidden {
40
+ display: none;
41
+ visibility: hidden;
42
+ }
43
+ .visible-phone {
44
+ display: none;
45
+ }
46
+ .visible-tablet {
47
+ display: none;
48
+ }
49
+ .visible-desktop {
50
+ display: block;
51
+ }
52
+ .hidden-phone {
53
+ display: block;
54
+ }
55
+ .hidden-tablet {
56
+ display: block;
57
+ }
58
+ .hidden-desktop {
59
+ display: none;
60
+ }
61
+ @media (max-width: 767px) {
62
+ .visible-phone {
63
+ display: block;
64
+ }
65
+ .hidden-phone {
66
+ display: none;
67
+ }
68
+ .hidden-desktop {
69
+ display: block;
70
+ }
71
+ .visible-desktop {
72
+ display: none;
73
+ }
74
+ }
75
+ @media (min-width: 768px) and (max-width: 979px) {
76
+ .visible-tablet {
77
+ display: block;
78
+ }
79
+ .hidden-tablet {
80
+ display: none;
81
+ }
82
+ .hidden-desktop {
83
+ display: block;
84
+ }
85
+ .visible-desktop {
86
+ display: none;
87
+ }
88
+ }
89
+ @media (max-width: 480px) {
90
+ .nav-collapse {
91
+ -webkit-transform: translate3d(0, 0, 0);
92
+ }
93
+ .page-header h1 small {
94
+ display: block;
95
+ line-height: 18px;
96
+ }
97
+ input[type="checkbox"],
98
+ input[type="radio"] {
99
+ border: 1px solid #ccc;
100
+ }
101
+ .form-horizontal .control-group > label {
102
+ float: none;
103
+ width: auto;
104
+ padding-top: 0;
105
+ text-align: left;
106
+ }
107
+ .form-horizontal .controls {
108
+ margin-left: 0;
109
+ }
110
+ .form-horizontal .control-list {
111
+ padding-top: 0;
112
+ }
113
+ .form-horizontal .form-actions {
114
+ padding-left: 10px;
115
+ padding-right: 10px;
116
+ }
117
+ .modal {
118
+ position: absolute;
119
+ top: 10px;
120
+ left: 10px;
121
+ right: 10px;
122
+ width: auto;
123
+ margin: 0;
124
+ }
125
+ .modal.fade.in {
126
+ top: auto;
127
+ }
128
+ .modal-header .close {
129
+ padding: 10px;
130
+ margin: -10px;
131
+ }
132
+ .carousel-caption {
133
+ position: static;
134
+ }
135
+ }
136
+ @media (max-width: 767px) {
137
+ body {
138
+ padding-left: 20px;
139
+ padding-right: 20px;
140
+ }
141
+ .navbar-fixed-top {
142
+ margin-left: -20px;
143
+ margin-right: -20px;
144
+ }
145
+ .container {
146
+ width: auto;
147
+ }
148
+ .row-fluid {
149
+ width: 100%;
150
+ }
151
+ .row {
152
+ margin-left: 0;
153
+ }
154
+ .row > [class*="span"],
155
+ .row-fluid > [class*="span"] {
156
+ float: none;
157
+ display: block;
158
+ width: auto;
159
+ margin: 0;
160
+ }
161
+ .thumbnails [class*="span"] {
162
+ width: auto;
163
+ }
164
+ input[class*="span"],
165
+ select[class*="span"],
166
+ textarea[class*="span"],
167
+ .uneditable-input {
168
+ display: block;
169
+ width: 100%;
170
+ min-height: 28px;
171
+ /* Make inputs at least the height of their button counterpart */
172
+
173
+ /* Makes inputs behave like true block-level elements */
174
+
175
+ -webkit-box-sizing: border-box;
176
+ -moz-box-sizing: border-box;
177
+ -ms-box-sizing: border-box;
178
+ box-sizing: border-box;
179
+ }
180
+ .input-prepend input[class*="span"],
181
+ .input-append input[class*="span"] {
182
+ width: auto;
183
+ }
184
+ }
185
+ @media (min-width: 768px) and (max-width: 979px) {
186
+ .row {
187
+ margin-left: -20px;
188
+ *zoom: 1;
189
+ }
190
+ .row:before,
191
+ .row:after {
192
+ display: table;
193
+ content: "";
194
+ }
195
+ .row:after {
196
+ clear: both;
197
+ }
198
+ [class*="span"] {
199
+ float: left;
200
+ margin-left: 20px;
201
+ }
202
+ .container,
203
+ .navbar-fixed-top .container,
204
+ .navbar-fixed-bottom .container {
205
+ width: 724px;
206
+ }
207
+ .span12 {
208
+ width: 724px;
209
+ }
210
+ .span11 {
211
+ width: 662px;
212
+ }
213
+ .span10 {
214
+ width: 600px;
215
+ }
216
+ .span9 {
217
+ width: 538px;
218
+ }
219
+ .span8 {
220
+ width: 476px;
221
+ }
222
+ .span7 {
223
+ width: 414px;
224
+ }
225
+ .span6 {
226
+ width: 352px;
227
+ }
228
+ .span5 {
229
+ width: 290px;
230
+ }
231
+ .span4 {
232
+ width: 228px;
233
+ }
234
+ .span3 {
235
+ width: 166px;
236
+ }
237
+ .span2 {
238
+ width: 104px;
239
+ }
240
+ .span1 {
241
+ width: 42px;
242
+ }
243
+ .offset12 {
244
+ margin-left: 764px;
245
+ }
246
+ .offset11 {
247
+ margin-left: 702px;
248
+ }
249
+ .offset10 {
250
+ margin-left: 640px;
251
+ }
252
+ .offset9 {
253
+ margin-left: 578px;
254
+ }
255
+ .offset8 {
256
+ margin-left: 516px;
257
+ }
258
+ .offset7 {
259
+ margin-left: 454px;
260
+ }
261
+ .offset6 {
262
+ margin-left: 392px;
263
+ }
264
+ .offset5 {
265
+ margin-left: 330px;
266
+ }
267
+ .offset4 {
268
+ margin-left: 268px;
269
+ }
270
+ .offset3 {
271
+ margin-left: 206px;
272
+ }
273
+ .offset2 {
274
+ margin-left: 144px;
275
+ }
276
+ .offset1 {
277
+ margin-left: 82px;
278
+ }
279
+ .row-fluid {
280
+ width: 100%;
281
+ *zoom: 1;
282
+ }
283
+ .row-fluid:before,
284
+ .row-fluid:after {
285
+ display: table;
286
+ content: "";
287
+ }
288
+ .row-fluid:after {
289
+ clear: both;
290
+ }
291
+ .row-fluid > [class*="span"] {
292
+ float: left;
293
+ margin-left: 2.762430939%;
294
+ }
295
+ .row-fluid > [class*="span"]:first-child {
296
+ margin-left: 0;
297
+ }
298
+ .row-fluid > .span12 {
299
+ width: 99.999999993%;
300
+ }
301
+ .row-fluid > .span11 {
302
+ width: 91.436464082%;
303
+ }
304
+ .row-fluid > .span10 {
305
+ width: 82.87292817100001%;
306
+ }
307
+ .row-fluid > .span9 {
308
+ width: 74.30939226%;
309
+ }
310
+ .row-fluid > .span8 {
311
+ width: 65.74585634900001%;
312
+ }
313
+ .row-fluid > .span7 {
314
+ width: 57.182320438000005%;
315
+ }
316
+ .row-fluid > .span6 {
317
+ width: 48.618784527%;
318
+ }
319
+ .row-fluid > .span5 {
320
+ width: 40.055248616%;
321
+ }
322
+ .row-fluid > .span4 {
323
+ width: 31.491712705%;
324
+ }
325
+ .row-fluid > .span3 {
326
+ width: 22.928176794%;
327
+ }
328
+ .row-fluid > .span2 {
329
+ width: 14.364640883%;
330
+ }
331
+ .row-fluid > .span1 {
332
+ width: 5.801104972%;
333
+ }
334
+ input,
335
+ textarea,
336
+ .uneditable-input {
337
+ margin-left: 0;
338
+ }
339
+ input.span12, textarea.span12, .uneditable-input.span12 {
340
+ width: 714px;
341
+ }
342
+ input.span11, textarea.span11, .uneditable-input.span11 {
343
+ width: 652px;
344
+ }
345
+ input.span10, textarea.span10, .uneditable-input.span10 {
346
+ width: 590px;
347
+ }
348
+ input.span9, textarea.span9, .uneditable-input.span9 {
349
+ width: 528px;
350
+ }
351
+ input.span8, textarea.span8, .uneditable-input.span8 {
352
+ width: 466px;
353
+ }
354
+ input.span7, textarea.span7, .uneditable-input.span7 {
355
+ width: 404px;
356
+ }
357
+ input.span6, textarea.span6, .uneditable-input.span6 {
358
+ width: 342px;
359
+ }
360
+ input.span5, textarea.span5, .uneditable-input.span5 {
361
+ width: 280px;
362
+ }
363
+ input.span4, textarea.span4, .uneditable-input.span4 {
364
+ width: 218px;
365
+ }
366
+ input.span3, textarea.span3, .uneditable-input.span3 {
367
+ width: 156px;
368
+ }
369
+ input.span2, textarea.span2, .uneditable-input.span2 {
370
+ width: 94px;
371
+ }
372
+ input.span1, textarea.span1, .uneditable-input.span1 {
373
+ width: 32px;
374
+ }
375
+ }
376
+ @media (max-width: 979px) {
377
+ body {
378
+ padding-top: 0;
379
+ }
380
+ .navbar-fixed-top {
381
+ position: static;
382
+ margin-bottom: 18px;
383
+ }
384
+ .navbar-fixed-top .navbar-inner {
385
+ padding: 5px;
386
+ }
387
+ .navbar .container {
388
+ width: auto;
389
+ padding: 0;
390
+ }
391
+ .navbar .brand {
392
+ padding-left: 10px;
393
+ padding-right: 10px;
394
+ margin: 0 0 0 -5px;
395
+ }
396
+ .navbar .nav-collapse {
397
+ clear: left;
398
+ }
399
+ .navbar .nav {
400
+ float: none;
401
+ margin: 0 0 9px;
402
+ }
403
+ .navbar .nav > li {
404
+ float: none;
405
+ }
406
+ .navbar .nav > li > a {
407
+ margin-bottom: 2px;
408
+ }
409
+ .navbar .nav > .divider-vertical {
410
+ display: none;
411
+ }
412
+ .navbar .nav .nav-header {
413
+ color: #999999;
414
+ text-shadow: none;
415
+ }
416
+ .navbar .nav > li > a,
417
+ .navbar .dropdown-menu a {
418
+ padding: 6px 15px;
419
+ font-weight: bold;
420
+ color: #999999;
421
+ -webkit-border-radius: 3px;
422
+ -moz-border-radius: 3px;
423
+ border-radius: 3px;
424
+ }
425
+ .navbar .dropdown-menu li + li a {
426
+ margin-bottom: 2px;
427
+ }
428
+ .navbar .nav > li > a:hover,
429
+ .navbar .dropdown-menu a:hover {
430
+ background-color: #222222;
431
+ }
432
+ .navbar .dropdown-menu {
433
+ position: static;
434
+ top: auto;
435
+ left: auto;
436
+ float: none;
437
+ display: block;
438
+ max-width: none;
439
+ margin: 0 15px;
440
+ padding: 0;
441
+ background-color: transparent;
442
+ border: none;
443
+ -webkit-border-radius: 0;
444
+ -moz-border-radius: 0;
445
+ border-radius: 0;
446
+ -webkit-box-shadow: none;
447
+ -moz-box-shadow: none;
448
+ box-shadow: none;
449
+ }
450
+ .navbar .dropdown-menu:before,
451
+ .navbar .dropdown-menu:after {
452
+ display: none;
453
+ }
454
+ .navbar .dropdown-menu .divider {
455
+ display: none;
456
+ }
457
+ .navbar-form,
458
+ .navbar-search {
459
+ float: none;
460
+ padding: 9px 15px;
461
+ margin: 9px 0;
462
+ border-top: 1px solid #222222;
463
+ border-bottom: 1px solid #222222;
464
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
465
+ -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
466
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
467
+ }
468
+ .navbar .nav.pull-right {
469
+ float: none;
470
+ margin-left: 0;
471
+ }
472
+ .navbar-static .navbar-inner {
473
+ padding-left: 10px;
474
+ padding-right: 10px;
475
+ }
476
+ .btn-navbar {
477
+ display: block;
478
+ }
479
+ .nav-collapse {
480
+ overflow: hidden;
481
+ height: 0;
482
+ }
483
+ }
484
+ @media (min-width: 980px) {
485
+ .nav-collapse.collapse {
486
+ height: auto !important;
487
+ overflow: visible !important;
488
+ }
489
+ }
490
+ @media (min-width: 1200px) {
491
+ .row {
492
+ margin-left: -30px;
493
+ *zoom: 1;
494
+ }
495
+ .row:before,
496
+ .row:after {
497
+ display: table;
498
+ content: "";
499
+ }
500
+ .row:after {
501
+ clear: both;
502
+ }
503
+ [class*="span"] {
504
+ float: left;
505
+ margin-left: 30px;
506
+ }
507
+ .container,
508
+ .navbar-fixed-top .container,
509
+ .navbar-fixed-bottom .container {
510
+ width: 1170px;
511
+ }
512
+ .span12 {
513
+ width: 1170px;
514
+ }
515
+ .span11 {
516
+ width: 1070px;
517
+ }
518
+ .span10 {
519
+ width: 970px;
520
+ }
521
+ .span9 {
522
+ width: 870px;
523
+ }
524
+ .span8 {
525
+ width: 770px;
526
+ }
527
+ .span7 {
528
+ width: 670px;
529
+ }
530
+ .span6 {
531
+ width: 570px;
532
+ }
533
+ .span5 {
534
+ width: 470px;
535
+ }
536
+ .span4 {
537
+ width: 370px;
538
+ }
539
+ .span3 {
540
+ width: 270px;
541
+ }
542
+ .span2 {
543
+ width: 170px;
544
+ }
545
+ .span1 {
546
+ width: 70px;
547
+ }
548
+ .offset12 {
549
+ margin-left: 1230px;
550
+ }
551
+ .offset11 {
552
+ margin-left: 1130px;
553
+ }
554
+ .offset10 {
555
+ margin-left: 1030px;
556
+ }
557
+ .offset9 {
558
+ margin-left: 930px;
559
+ }
560
+ .offset8 {
561
+ margin-left: 830px;
562
+ }
563
+ .offset7 {
564
+ margin-left: 730px;
565
+ }
566
+ .offset6 {
567
+ margin-left: 630px;
568
+ }
569
+ .offset5 {
570
+ margin-left: 530px;
571
+ }
572
+ .offset4 {
573
+ margin-left: 430px;
574
+ }
575
+ .offset3 {
576
+ margin-left: 330px;
577
+ }
578
+ .offset2 {
579
+ margin-left: 230px;
580
+ }
581
+ .offset1 {
582
+ margin-left: 130px;
583
+ }
584
+ .row-fluid {
585
+ width: 100%;
586
+ *zoom: 1;
587
+ }
588
+ .row-fluid:before,
589
+ .row-fluid:after {
590
+ display: table;
591
+ content: "";
592
+ }
593
+ .row-fluid:after {
594
+ clear: both;
595
+ }
596
+ .row-fluid > [class*="span"] {
597
+ float: left;
598
+ margin-left: 2.564102564%;
599
+ }
600
+ .row-fluid > [class*="span"]:first-child {
601
+ margin-left: 0;
602
+ }
603
+ .row-fluid > .span12 {
604
+ width: 100%;
605
+ }
606
+ .row-fluid > .span11 {
607
+ width: 91.45299145300001%;
608
+ }
609
+ .row-fluid > .span10 {
610
+ width: 82.905982906%;
611
+ }
612
+ .row-fluid > .span9 {
613
+ width: 74.358974359%;
614
+ }
615
+ .row-fluid > .span8 {
616
+ width: 65.81196581200001%;
617
+ }
618
+ .row-fluid > .span7 {
619
+ width: 57.264957265%;
620
+ }
621
+ .row-fluid > .span6 {
622
+ width: 48.717948718%;
623
+ }
624
+ .row-fluid > .span5 {
625
+ width: 40.170940171000005%;
626
+ }
627
+ .row-fluid > .span4 {
628
+ width: 31.623931624%;
629
+ }
630
+ .row-fluid > .span3 {
631
+ width: 23.076923077%;
632
+ }
633
+ .row-fluid > .span2 {
634
+ width: 14.529914530000001%;
635
+ }
636
+ .row-fluid > .span1 {
637
+ width: 5.982905983%;
638
+ }
639
+ input,
640
+ textarea,
641
+ .uneditable-input {
642
+ margin-left: 0;
643
+ }
644
+ input.span12, textarea.span12, .uneditable-input.span12 {
645
+ width: 1160px;
646
+ }
647
+ input.span11, textarea.span11, .uneditable-input.span11 {
648
+ width: 1060px;
649
+ }
650
+ input.span10, textarea.span10, .uneditable-input.span10 {
651
+ width: 960px;
652
+ }
653
+ input.span9, textarea.span9, .uneditable-input.span9 {
654
+ width: 860px;
655
+ }
656
+ input.span8, textarea.span8, .uneditable-input.span8 {
657
+ width: 760px;
658
+ }
659
+ input.span7, textarea.span7, .uneditable-input.span7 {
660
+ width: 660px;
661
+ }
662
+ input.span6, textarea.span6, .uneditable-input.span6 {
663
+ width: 560px;
664
+ }
665
+ input.span5, textarea.span5, .uneditable-input.span5 {
666
+ width: 460px;
667
+ }
668
+ input.span4, textarea.span4, .uneditable-input.span4 {
669
+ width: 360px;
670
+ }
671
+ input.span3, textarea.span3, .uneditable-input.span3 {
672
+ width: 260px;
673
+ }
674
+ input.span2, textarea.span2, .uneditable-input.span2 {
675
+ width: 160px;
676
+ }
677
+ input.span1, textarea.span1, .uneditable-input.span1 {
678
+ width: 60px;
679
+ }
680
+ .thumbnails {
681
+ margin-left: -30px;
682
+ }
683
+ .thumbnails > li {
684
+ margin-left: 30px;
685
+ }
686
+ }