gated_release 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -0
  3. data/CHANGELOG.md +22 -0
  4. data/Gemfile.lock +54 -6
  5. data/MIT-LICENSE +20 -0
  6. data/README.md +32 -1
  7. data/Rakefile +25 -2
  8. data/app/assets/config/gated_release_manifest.js +2 -0
  9. data/app/assets/images/gated_release/.keep +0 -0
  10. data/app/assets/javascripts/gated_release/application.js +13 -0
  11. data/app/assets/stylesheets/gated_release/application.css +15 -0
  12. data/app/assets/stylesheets/gated_release/gates.css +131 -0
  13. data/app/controllers/gated_release/application_controller.rb +5 -0
  14. data/app/controllers/gated_release/gates_controller.rb +47 -0
  15. data/app/helpers/gated_release/application_helper.rb +12 -0
  16. data/app/jobs/gated_release/application_job.rb +4 -0
  17. data/app/mailers/gated_release/application_mailer.rb +6 -0
  18. data/app/models/gated_release/application_record.rb +5 -0
  19. data/{lib → app/models}/gated_release/gate.rb +9 -2
  20. data/app/views/gated_release/gates/index.html.erb +64 -0
  21. data/app/views/layouts/gated_release/application.html.erb +18 -0
  22. data/config/routes.rb +11 -0
  23. data/gated_release.gemspec +6 -3
  24. data/lib/gated_release.rb +1 -1
  25. data/lib/gated_release/engine.rb +13 -0
  26. data/lib/gated_release/version.rb +1 -1
  27. data/lib/tasks/gated_release_tasks.rake +4 -0
  28. data/spec/dummy/Rakefile +6 -0
  29. data/spec/dummy/app/assets/config/manifest.js +5 -0
  30. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  31. data/spec/dummy/app/assets/javascripts/cable.js +13 -0
  32. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  33. data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
  34. data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
  35. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  36. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  37. data/spec/dummy/app/jobs/application_job.rb +2 -0
  38. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  39. data/spec/dummy/app/models/application_record.rb +3 -0
  40. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  41. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  42. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  43. data/spec/dummy/bin/bundle +3 -0
  44. data/spec/dummy/bin/rails +4 -0
  45. data/spec/dummy/bin/rake +4 -0
  46. data/spec/dummy/bin/setup +34 -0
  47. data/spec/dummy/bin/update +29 -0
  48. data/spec/dummy/config.ru +5 -0
  49. data/spec/dummy/config/application.rb +15 -0
  50. data/spec/dummy/config/boot.rb +5 -0
  51. data/spec/dummy/config/cable.yml +9 -0
  52. data/spec/dummy/config/database.yml +25 -0
  53. data/spec/dummy/config/environment.rb +5 -0
  54. data/spec/dummy/config/environments/development.rb +54 -0
  55. data/spec/dummy/config/environments/production.rb +86 -0
  56. data/spec/dummy/config/environments/test.rb +42 -0
  57. data/spec/dummy/config/initializers/application_controller_renderer.rb +6 -0
  58. data/spec/dummy/config/initializers/assets.rb +11 -0
  59. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  60. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  61. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  62. data/spec/dummy/config/initializers/inflections.rb +16 -0
  63. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  64. data/spec/dummy/config/initializers/new_framework_defaults.rb +24 -0
  65. data/spec/dummy/config/initializers/session_store.rb +3 -0
  66. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  67. data/spec/dummy/config/locales/en.yml +23 -0
  68. data/spec/dummy/config/puma.rb +47 -0
  69. data/spec/dummy/config/routes.rb +3 -0
  70. data/spec/dummy/config/secrets.yml +22 -0
  71. data/spec/dummy/config/spring.rb +6 -0
  72. data/spec/dummy/db/schema.rb +15 -0
  73. data/spec/dummy/public/404.html +67 -0
  74. data/spec/dummy/public/422.html +67 -0
  75. data/spec/dummy/public/500.html +66 -0
  76. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  77. data/spec/dummy/public/apple-touch-icon.png +0 -0
  78. data/spec/dummy/public/favicon.ico +0 -0
  79. data/spec/gated_release_spec.rb +8 -0
  80. data/spec/generators/install/install_generator_spec.rb +25 -0
  81. data/spec/models/gated_release/gate_spec.rb +293 -0
  82. data/spec/spec_helper.rb +36 -0
  83. metadata +160 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 76ee56e30f5bec17b5b0b170ae36c54c1890eae0
4
- data.tar.gz: 11a70be7aed859789307ccf302bd5423efac9a75
3
+ metadata.gz: 8d2923b054974e2efa9fecc05808c04a04f823f7
4
+ data.tar.gz: 8cccd31a0145cf092869e85bd17848f12250911f
5
5
  SHA512:
6
- metadata.gz: 92d02bd595b2df90cbdf2f826f20433f16d41409f8ea1d58499edf9a6aa0f7c82b8553a8f9f9648d3cc02cae3f9de8499a1e24e07c52dcb6d41b733654160db8
7
- data.tar.gz: fe7ac538785a7247b5a5bdc7c5dea596a549afe665f85fe5a29bd3cf81edaf5d8724c5fb7092a7768104fbb71ef156a357ffe52838869dc92f102ce5483b31fe
6
+ metadata.gz: 9b3113d3cc9e160107521f8b7a1a81bf9ed11a2fe4f400b3c35c0b1adb98d2b809598df8dfbd330a073e35e5e9aa3df53b49db585da073c3386363df40a6dbd8
7
+ data.tar.gz: e9ca556223904d115d4dbd616a6300c71d6b8c8fdbfb5363102cfeda5a8ba9dc349b27f732f236ff5a2087bc972fd2c63e732b3c27fdb787d84bb462dd1d45b4
data/.gitignore CHANGED
@@ -3,9 +3,15 @@
3
3
  /_yardoc/
4
4
  /coverage/
5
5
  /doc/
6
+ log/*.log
6
7
  /pkg/
7
8
  /spec/reports/
8
9
  /tmp/
10
+ /vendor/
11
+ spec/dummy/db/*.sqlite3
12
+ spec/dummy/db/*.sqlite3-journal
13
+ spec/dummy/log/*.log
14
+ spec/dummy/tmp/
9
15
 
10
16
  # rspec failure tracking
11
17
  .rspec_status
data/CHANGELOG.md ADDED
@@ -0,0 +1,22 @@
1
+ Change Log
2
+ ==========
3
+
4
+ All notable changes to this project will be documented in this file.
5
+
6
+ The format is based on [Keep a Changelog](http://keepachangelog.com/)
7
+ and this project adheres to [Semantic Versioning](http://semver.org/).
8
+
9
+ ## [0.0.2] - 2017-03-17
10
+
11
+ ### Added
12
+
13
+ - Admin panel using a Rails engine
14
+
15
+ ## 0.0.1 - 2017-03-17
16
+
17
+ ### Added
18
+
19
+ - Allow to manage gates using the Rails console
20
+ - Initial release :)
21
+
22
+ [0.2.0]: https://github.com/redbubble/gated_release/compare/v0.1.0...v0.2.0
data/Gemfile.lock CHANGED
@@ -1,12 +1,23 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gated_release (0.1.0)
4
+ gated_release (0.2.0)
5
5
  activerecord (>= 4.0, < 6)
6
+ rails (>= 4.0, < 6)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
9
10
  specs:
11
+ actioncable (5.0.2)
12
+ actionpack (= 5.0.2)
13
+ nio4r (>= 1.2, < 3.0)
14
+ websocket-driver (~> 0.6.1)
15
+ actionmailer (5.0.2)
16
+ actionpack (= 5.0.2)
17
+ actionview (= 5.0.2)
18
+ activejob (= 5.0.2)
19
+ mail (~> 2.5, >= 2.5.4)
20
+ rails-dom-testing (~> 2.0)
10
21
  actionpack (5.0.2)
11
22
  actionview (= 5.0.2)
12
23
  activesupport (= 5.0.2)
@@ -20,6 +31,9 @@ GEM
20
31
  erubis (~> 2.7.0)
21
32
  rails-dom-testing (~> 2.0)
22
33
  rails-html-sanitizer (~> 1.0, >= 1.0.3)
34
+ activejob (5.0.2)
35
+ activesupport (= 5.0.2)
36
+ globalid (>= 0.3.6)
23
37
  activemodel (5.0.2)
24
38
  activesupport (= 5.0.2)
25
39
  activerecord (5.0.2)
@@ -40,17 +54,37 @@ GEM
40
54
  generator_spec (0.9.3)
41
55
  activesupport (>= 3.0.0)
42
56
  railties (>= 3.0.0)
57
+ globalid (0.3.7)
58
+ activesupport (>= 4.1.0)
43
59
  i18n (0.8.1)
44
60
  loofah (2.0.3)
45
61
  nokogiri (>= 1.5.9)
62
+ mail (2.6.4)
63
+ mime-types (>= 1.16, < 4)
46
64
  method_source (0.8.2)
65
+ mime-types (3.1)
66
+ mime-types-data (~> 3.2015)
67
+ mime-types-data (3.2016.0521)
47
68
  mini_portile2 (2.1.0)
48
69
  minitest (5.10.1)
70
+ nio4r (2.0.0)
49
71
  nokogiri (1.7.0.1)
50
72
  mini_portile2 (~> 2.1.0)
51
73
  rack (2.0.1)
52
74
  rack-test (0.6.3)
53
75
  rack (>= 1.0)
76
+ rails (5.0.2)
77
+ actioncable (= 5.0.2)
78
+ actionmailer (= 5.0.2)
79
+ actionpack (= 5.0.2)
80
+ actionview (= 5.0.2)
81
+ activejob (= 5.0.2)
82
+ activemodel (= 5.0.2)
83
+ activerecord (= 5.0.2)
84
+ activesupport (= 5.0.2)
85
+ bundler (>= 1.3.0, < 2.0)
86
+ railties (= 5.0.2)
87
+ sprockets-rails (>= 2.0.0)
54
88
  rails-dom-testing (2.0.2)
55
89
  activesupport (>= 4.2.0, < 6.0)
56
90
  nokogiri (~> 1.6)
@@ -63,10 +97,6 @@ GEM
63
97
  rake (>= 0.8.7)
64
98
  thor (>= 0.18.1, < 2.0)
65
99
  rake (10.5.0)
66
- rspec (3.5.0)
67
- rspec-core (~> 3.5.0)
68
- rspec-expectations (~> 3.5.0)
69
- rspec-mocks (~> 3.5.0)
70
100
  rspec-core (3.5.4)
71
101
  rspec-support (~> 3.5.0)
72
102
  rspec-expectations (3.5.0)
@@ -75,12 +105,30 @@ GEM
75
105
  rspec-mocks (3.5.0)
76
106
  diff-lcs (>= 1.2.0, < 2.0)
77
107
  rspec-support (~> 3.5.0)
108
+ rspec-rails (3.5.2)
109
+ actionpack (>= 3.0)
110
+ activesupport (>= 3.0)
111
+ railties (>= 3.0)
112
+ rspec-core (~> 3.5.0)
113
+ rspec-expectations (~> 3.5.0)
114
+ rspec-mocks (~> 3.5.0)
115
+ rspec-support (~> 3.5.0)
78
116
  rspec-support (3.5.0)
117
+ sprockets (3.7.1)
118
+ concurrent-ruby (~> 1.0)
119
+ rack (> 1, < 3)
120
+ sprockets-rails (3.2.0)
121
+ actionpack (>= 4.0)
122
+ activesupport (>= 4.0)
123
+ sprockets (>= 3.0.0)
79
124
  sqlite3 (1.3.13)
80
125
  thor (0.19.4)
81
126
  thread_safe (0.3.6)
82
127
  tzinfo (1.2.2)
83
128
  thread_safe (~> 0.1)
129
+ websocket-driver (0.6.5)
130
+ websocket-extensions (>= 0.1.0)
131
+ websocket-extensions (0.1.2)
84
132
 
85
133
  PLATFORMS
86
134
  ruby
@@ -91,7 +139,7 @@ DEPENDENCIES
91
139
  gated_release!
92
140
  generator_spec (~> 0.9.0)
93
141
  rake (~> 10.0)
94
- rspec (~> 3.0)
142
+ rspec-rails (~> 3.0)
95
143
  sqlite3 (~> 1.3)
96
144
 
97
145
  BUNDLED WITH
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2017 David Carlin
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # GatedRelease
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/gated_release.svg)](http://badge.fury.io/rb/gated_release)
4
+ [![Build Status](https://travis-ci.org/redbubble/gated_release.svg?branch=master)](https://travis-ci.org/redbubble/gated_release)
5
+ [![Code Climate](https://codeclimate.com/github/redbubble/gated_release.svg)](https://codeclimate.com/github/redbubble/gated/release)
6
+ [![Inline docs](http://inch-ci.org/github/redbubble/gated_release.svg?branch=master)](http://inch-ci.org/github/redbubble/gated_release)
7
+
3
8
  This gem allows you to easily manage split code paths in your application without redeploys.
4
9
 
5
10
  ## Installation
@@ -9,7 +14,7 @@ This gem allows you to easily manage split code paths in your application withou
9
14
  Add this line to your application's Gemfile:
10
15
 
11
16
  ```ruby
12
- gem 'gated_release', '0.1.0' # see semver.org
17
+ gem 'gated_release', '0.2.0' # see semver.org
13
18
  ```
14
19
 
15
20
  And then execute:
@@ -29,6 +34,14 @@ $ bundle exec rails generate gated_release:install
29
34
  $ bundle exec rake db:migrate
30
35
  ```
31
36
 
37
+ Mount it in your config/routes.rb file
38
+
39
+ ```ruby
40
+ mount GatedRelease::Engine => "/gated_release"
41
+ ```
42
+
43
+ and visit `http://localhost:3000/gated_release`
44
+
32
45
  ## Usage
33
46
 
34
47
  ```ruby
@@ -38,6 +51,20 @@ GatedRelease::Gate.get('gate-name').run(
38
51
  )
39
52
  ```
40
53
 
54
+ ```ruby
55
+ # Close the gate on any error from the open code path
56
+ GatedRelease::Gate.get('gate-name').run(
57
+ open: -> { code_to_run_for_open_gate },
58
+ closed: -> { code_to_run_for_closed_gate },
59
+ close_on_error: true
60
+ )
61
+ ```
62
+
63
+
64
+ ### Managing Gates
65
+ Gates can be managed from the page: `http://localhost:3000/gated_release`.
66
+ They can also be manually modified with the following commands:
67
+
41
68
  To open the gate:
42
69
  ```ruby
43
70
  GatedRelease::Gate.get('gate-name').open!
@@ -58,6 +85,10 @@ To put the gate into 'percentage' state, where a set percentage of code executio
58
85
  GatedRelease::Gate.get('gate-name').percentage!(10)
59
86
  ```
60
87
 
88
+ ## Credits
89
+
90
+ Thanks to John Sardine for the table css: http://johnsardine.com/freebies/dl-html-css/simple-little-tab/
91
+
61
92
  ## Development
62
93
 
63
94
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/Rakefile CHANGED
@@ -1,6 +1,29 @@
1
- require "bundler/gem_tasks"
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'bundler/gem_tasks'
2
8
  require "rspec/core/rake_task"
9
+ require 'rdoc/task'
10
+
11
+ RSpec::Core::RakeTask.new(spec: 'app:db:test:prepare')
12
+
13
+ RDoc::Task.new(:rdoc) do |rdoc|
14
+ rdoc.rdoc_dir = 'rdoc'
15
+ rdoc.title = 'GatedRelease'
16
+ rdoc.options << '--line-numbers'
17
+ rdoc.rdoc_files.include('README.md')
18
+ rdoc.rdoc_files.include('lib/**/*.rb')
19
+ end
20
+
21
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
22
+ load 'rails/tasks/engine.rake'
23
+
24
+ Bundler::GemHelper.install_tasks
25
+
3
26
 
4
- RSpec::Core::RakeTask.new(:spec)
27
+ load 'rails/tasks/statistics.rake'
5
28
 
6
29
  task :default => :spec
@@ -0,0 +1,2 @@
1
+ //= link_directory ../javascripts/gated_release .js
2
+ //= link_directory ../stylesheets/gated_release .css
File without changes
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file. JavaScript code in this file should be added after the last require_* statement.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require 'gated_release/gates'
14
+ *= require_self
15
+ */
@@ -0,0 +1,131 @@
1
+ body {
2
+ margin: 0;
3
+ font-family: Arial, Helvetica, sans-serif;
4
+ font-size: 14px;
5
+ }
6
+
7
+ input[type=submit] {
8
+ padding: 10px;
9
+ }
10
+
11
+ .alert {
12
+ width: 100%;
13
+ text-align: center;
14
+ background-color: #f0f6ff;
15
+ padding: 8px;
16
+ border-bottom: 1px solid #72b2ff;
17
+ color: #333;
18
+ font-size: 110%;
19
+
20
+ }
21
+
22
+ #gates {
23
+ color:#666;
24
+ text-shadow: 1px 1px 0px #fff;
25
+ margin: 20px;
26
+ }
27
+ .gated-release-search-form {
28
+ float: right;
29
+ }
30
+
31
+ .gated-release-heading {
32
+ float: left;
33
+ margin: 0 0 10px 0;
34
+ }
35
+
36
+ /* http://johnsardine.com/freebies/dl-html-css/simple-little-tab/ */
37
+ table a:link {
38
+ color: #666;
39
+ font-weight: bold;
40
+ text-decoration:none;
41
+ }
42
+ table a:visited {
43
+ color: #999999;
44
+ font-weight:bold;
45
+ text-decoration:none;
46
+ }
47
+ table a:active,
48
+ table a:hover {
49
+ color: #bd5a35;
50
+ text-decoration:underline;
51
+ }
52
+
53
+ table {
54
+ width: 100%;
55
+ font-size: 1em;
56
+ background:#eaebec;
57
+ border:#ccc 1px solid;
58
+
59
+ -moz-border-radius:3px;
60
+ -webkit-border-radius:3px;
61
+ border-radius:3px;
62
+
63
+ -moz-box-shadow: 0 1px 2px #d1d1d1;
64
+ -webkit-box-shadow: 0 1px 2px #d1d1d1;
65
+ box-shadow: 0 1px 2px #d1d1d1;
66
+ }
67
+ table th {
68
+ padding:21px 25px 22px 25px;
69
+ border-top:1px solid #fafafa;
70
+ border-bottom:1px solid #e0e0e0;
71
+
72
+ background: #ededed;
73
+ background: -webkit-gradient(linear, left top, left bottom, from(#ededed), to(#ebebeb));
74
+ background: -moz-linear-gradient(top, #ededed, #ebebeb);
75
+ }
76
+ table th:first-child {
77
+ text-align: left;
78
+ padding-left:20px;
79
+ }
80
+ table tr:first-child th:first-child {
81
+ -moz-border-radius-topleft:3px;
82
+ -webkit-border-top-left-radius:3px;
83
+ border-top-left-radius:3px;
84
+ }
85
+ table tr:first-child th:last-child {
86
+ -moz-border-radius-topright:3px;
87
+ -webkit-border-top-right-radius:3px;
88
+ border-top-right-radius:3px;
89
+ }
90
+ table tr {
91
+ text-align: center;
92
+ padding-left:20px;
93
+ }
94
+ table td:first-child {
95
+ text-align: left;
96
+ padding-left:20px;
97
+ border-left: 0;
98
+ }
99
+ table td {
100
+ padding:18px;
101
+ border-top: 1px solid #ffffff;
102
+ border-bottom:1px solid #e0e0e0;
103
+ border-left: 1px solid #e0e0e0;
104
+
105
+ background: #fafafa;
106
+ background: -webkit-gradient(linear, left top, left bottom, from(#fbfbfb), to(#fafafa));
107
+ background: -moz-linear-gradient(top, #fbfbfb, #fafafa);
108
+ }
109
+ table tr:nth-child(even) td {
110
+ background: #f6f6f6;
111
+ background: -webkit-gradient(linear, left top, left bottom, from(#f8f8f8), to(#f6f6f6));
112
+ background: -moz-linear-gradient(top, #f8f8f8, #f6f6f6);
113
+ }
114
+ table tr:last-child td {
115
+ border-bottom:0;
116
+ }
117
+ table tr:last-child td:first-child {
118
+ -moz-border-radius-bottomleft:3px;
119
+ -webkit-border-bottom-left-radius:3px;
120
+ border-bottom-left-radius:3px;
121
+ }
122
+ table tr:last-child td:last-child {
123
+ -moz-border-radius-bottomright:3px;
124
+ -webkit-border-bottom-right-radius:3px;
125
+ border-bottom-right-radius:3px;
126
+ }
127
+ table tr:hover td {
128
+ background: #f2f2f2;
129
+ background: -webkit-gradient(linear, left top, left bottom, from(#f2f2f2), to(#f0f0f0));
130
+ background: -moz-linear-gradient(top, #f2f2f2, #f0f0f0);
131
+ }