validation_auditor 0.2.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: df3fdbc82541c85c7c13bddebe3a18a4a7b8d50e
4
- data.tar.gz: 698944970c971bf99d30eeec61782f75496e6c3d
3
+ metadata.gz: c2e09a496b0f32341947809d1ab6f87d2216472f
4
+ data.tar.gz: 8631c7538cde8a75ebc52520b00616e75d9c1805
5
5
  SHA512:
6
- metadata.gz: bf9fd4135a3a550911658e1670a1bfa16080503dbca7a683c0da183da04553149cb4a0d65267430dd2267b2e1a3e525ff659b88eeea7328e42dbdba7e5cae96a
7
- data.tar.gz: 0b93eba5ec3321b8fb09cf5b9e31ea695312a9daf08a31ceb637a083649c770a618b17f555d6adf4a299cbf18185154ca3c7ee63a543eda015de669724417352
6
+ metadata.gz: f41005ee1a54818d95c261d04f3531d36ab3011b89914d935e47bb19c3499120108e6618cb03fe2cf63db929914e50ea0f03c154e36c84b6ea178016dd627dd6
7
+ data.tar.gz: 35c20fbf0e1d8d00b86c8190107e0cbc7b6dd9bf605479bc60df49ec539b6cf4c781500b058644820ebb2c8f9ce68a2c666c879a13be9b4e5d5925e1e7bc263c
data/.travis.yml CHANGED
@@ -3,7 +3,13 @@ rvm:
3
3
  - "1.9.3"
4
4
  - "2.0.0"
5
5
  - "2.1.2"
6
+ - "2.1.3"
7
+ - "2.1.4"
6
8
  gemfile:
7
- - gemfiles/rails_3_2.Gemfile
8
- - gemfiles/rails_4_0.Gemfile
9
- - gemfiles/rails_4_1.Gemfile
9
+ - gemfiles/rails_3_2.gemfile
10
+ - gemfiles/rails_4_0.gemfile
11
+ - gemfiles/rails_4_1.gemfile
12
+ addons:
13
+ code_climate:
14
+ repo_token:
15
+ secure: "JmFIrG06+Df5MZoksMx/H/g1Z/oe8cZaJMP6zYSiZ5uz122t6NF6Rs1JPDJeXP2+YMwsuSp7Ge45arFytGwysSSvY2iGGlA+om2IDekMsqqhsVYbo1rFzbM+H/RfrPUU2mY6MYkAmHzIBQvYUAWQ4s3qbHaCXXsdEyoilc99uaU="
data/Appraisals ADDED
@@ -0,0 +1,19 @@
1
+ # Copyright © 2014, Watu
2
+
3
+ appraise "rails-3_2" do
4
+ gem "activerecord", "~> 3.2.0"
5
+ gem "actionpack", "~> 3.2.0"
6
+ gem "railties", "~> 3.2.0"
7
+ end
8
+
9
+ appraise "rails-4_0" do
10
+ gem "activerecord", "~> 4.0.0"
11
+ gem "actionpack", "~> 4.0.0"
12
+ gem "railties", "~> 4.0.0"
13
+ end
14
+
15
+ appraise "rails-4_1" do
16
+ gem "activerecord", "~> 4.1.0"
17
+ gem "actionpack", "~> 4.1.0"
18
+ gem "railties", "~> 4.1.0"
19
+ end
data/CHANGELOG.md CHANGED
@@ -1,15 +1,19 @@
1
- ## validation_auditor 0.2.1 (Sep 5, 2014) ##
1
+ Authoritative changelog in README.md.
2
2
 
3
- * When cleaning params to save them as yaml, also clean each element of arrays.
3
+ ## validation_auditor 1.0.0 (Nov 17, 2014)
4
+ - Started testing Ruby 2.1.3 and 2.1.4.
5
+ - Refactoring to make code more readable (increased code climate to 4.0).
6
+ - Marked internal methods as private.
7
+ - Improved documentation.
4
8
 
5
- ## validation_auditor 0.2.0 (Jul 23, 2014) ##
9
+ ## validation_auditor 0.2.1 (Sep 5, 2014) ##
10
+ - When cleaning params to save them as yaml, also clean each element of arrays.
6
11
 
7
- * Respect the filter_parameters configuration from Rails.
12
+ ## validation_auditor 0.2.0 (Jul 23, 2014) ##
13
+ - Respect the filter_parameters configuration from Rails.
8
14
 
9
15
  ## validation_auditor 0.1.1 (Jul 23, 2014) ##
10
-
11
- * Don't crash in the presence of file uploads when reporting validation errors.
16
+ - Don't crash in the presence of file uploads when reporting validation errors.
12
17
 
13
18
  ## validation_auditor 0.1.0 (January 8, 2014) ##
14
-
15
- * Everything
19
+ - Everything.
data/Gemfile CHANGED
@@ -3,16 +3,4 @@
3
3
 
4
4
  source "https://rubygems.org"
5
5
 
6
- gem "assert_difference"
7
- gem "bundler", "~> 1.3"
8
- gem "coveralls", require: false
9
- gem "minitest"
10
- gem "minitest-reporters"
11
- gem "minitest-rails"
12
- gem "rake"
13
- gem "simplecov"
14
- gem "shoulda"
15
- gem "sqlite3"
16
- gem "activerecord", "~> 3.2.0"
17
- gem "actionpack", "~> 3.2.0"
18
- gem "railties", "~> 3.2.0"
6
+ gemspec
data/README.md CHANGED
@@ -3,7 +3,9 @@
3
3
  [![Build Status](https://travis-ci.org/watu/validation_auditor.png?branch=master)](https://travis-ci.org/watu/validation_auditor)
4
4
  [![Coverage Status](https://coveralls.io/repos/watu/validation_auditor/badge.png?branch=master)](https://coveralls.io/r/watu/validation_auditor?branch=master)
5
5
  [![Code Climate](https://codeclimate.com/github/watu/validation_auditor.png)](https://codeclimate.com/github/watu/validation_auditor)
6
+ [![Inline docs](http://inch-ci.org/github/watu/validation_auditor.png?branch=master)](http://inch-ci.org/github/watu/validation_auditor)
6
7
  [![Gem Version](https://badge.fury.io/rb/validation_auditor.png)](http://badge.fury.io/rb/validation_auditor)
8
+ [![Dependency Status](https://gemnasium.com/watu/validation_auditor.svg)](https://gemnasium.com/watu/validation_auditor)
7
9
 
8
10
  A user visits your web app, tries to do something with it but it fails due to a validation error. Generally, the
9
11
  validation is stopping a user from doing something bad, but every now and then it's the validation that is bad. Don't
@@ -52,6 +54,43 @@ If you enable validation audit on the controller, by calling `audit_validation_e
52
54
  then you'll also get params, url and user agent in the validation audit. This breaks the model-controller separation, so
53
55
  it's optional.
54
56
 
57
+ If for some reason saving a validation audit fails, the exception will be left to propagate into your application so
58
+ that no exception is silently swallowed. You may want to not let a secondary system, like auditing, to stop your
59
+ application for working (depending on how critical auditing is for you). If that's the case, you can define an
60
+ exception handler that can report in whatever fashion you normally report exceptions to your dev team or silently
61
+ swallow the exception. This may or may not work in Rails < 4.
62
+
63
+ ValidationAuditor.exception_handler = lambda do |e, va|
64
+ puts "When trying to save validation audit #{va}, exception #{e} was encountered."
65
+ end
66
+
67
+ ## Users
68
+
69
+ This gem is being used by:
70
+
71
+ - [Watu](https://watuapp.com)
72
+ - You? please, let us know if you are using this gem.
73
+
74
+ ## Changelog
75
+
76
+ ### validation_auditor 1.0.0 (Nov 17, 2014)
77
+ - Started testing Ruby 2.1.3 and 2.1.4.
78
+ - Refactoring to make code more readable (increased code climate to 4.0).
79
+ - Marked internal methods as private.
80
+ - Improved documentation.
81
+
82
+ ### validation_auditor 0.2.1 (Sep 5, 2014)
83
+ - When cleaning params to save them as yaml, also clean each element of arrays.
84
+
85
+ ### validation_auditor 0.2.0 (Jul 23, 2014)
86
+ - Respect the filter_parameters configuration from Rails.
87
+
88
+ ### validation_auditor 0.1.1 (Jul 23, 2014)
89
+ - Don't crash in the presence of file uploads when reporting validation errors.
90
+
91
+ ### validation_auditor 0.1.0 (January 8, 2014)
92
+ - Everything.
93
+
55
94
  ## Contributing
56
95
 
57
96
  1. Fork it
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
1
  # encoding: UTF-8
2
2
  # Copyright © 2014, Watu
3
3
 
4
+ require "rubygems"
5
+ require "bundler/setup"
4
6
  require "bundler/gem_tasks"
5
7
 
6
8
  require "rake/testtask"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 3.2.0"
6
+ gem "actionpack", "~> 3.2.0"
7
+ gem "railties", "~> 3.2.0"
8
+
9
+ gemspec :path => "../"
@@ -0,0 +1,141 @@
1
+ PATH
2
+ remote: ../
3
+ specs:
4
+ validation_auditor (1.0.0)
5
+ actionpack (>= 3.0.0)
6
+ activerecord (>= 3.0.0)
7
+ railties (>= 3.0.0)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ actionpack (3.2.19)
13
+ activemodel (= 3.2.19)
14
+ activesupport (= 3.2.19)
15
+ builder (~> 3.0.0)
16
+ erubis (~> 2.7.0)
17
+ journey (~> 1.0.4)
18
+ rack (~> 1.4.5)
19
+ rack-cache (~> 1.2)
20
+ rack-test (~> 0.6.1)
21
+ sprockets (~> 2.2.1)
22
+ activemodel (3.2.19)
23
+ activesupport (= 3.2.19)
24
+ builder (~> 3.0.0)
25
+ activerecord (3.2.19)
26
+ activemodel (= 3.2.19)
27
+ activesupport (= 3.2.19)
28
+ arel (~> 3.0.2)
29
+ tzinfo (~> 0.3.29)
30
+ activesupport (3.2.19)
31
+ i18n (~> 0.6, >= 0.6.4)
32
+ multi_json (~> 1.0)
33
+ ansi (1.4.3)
34
+ appraisal (1.0.2)
35
+ bundler
36
+ rake
37
+ thor (>= 0.14.0)
38
+ arel (3.0.3)
39
+ assert_difference (0.5.0)
40
+ builder (3.0.4)
41
+ codeclimate-test-reporter (0.4.1)
42
+ simplecov (>= 0.7.1, < 1.0.0)
43
+ coveralls (0.7.1)
44
+ multi_json (~> 1.3)
45
+ rest-client
46
+ simplecov (>= 0.7)
47
+ term-ansicolor
48
+ thor
49
+ docile (1.1.5)
50
+ erubis (2.7.0)
51
+ hashie (3.3.1)
52
+ hike (1.2.3)
53
+ i18n (0.6.11)
54
+ journey (1.0.4)
55
+ json (1.8.1)
56
+ metaclass (0.0.4)
57
+ mime-types (1.25.1)
58
+ minitest (4.7.5)
59
+ minitest-rails (1.0.1)
60
+ minitest (~> 4.7)
61
+ minitest-test (~> 1.0)
62
+ railties (>= 3.0, < 4.1)
63
+ minitest-reporters (0.14.24)
64
+ ansi
65
+ builder
66
+ minitest (>= 2.12, < 5.0)
67
+ powerbar
68
+ minitest-test (1.1.0)
69
+ minitest (~> 4.0)
70
+ mocha (1.1.0)
71
+ metaclass (~> 0.0.1)
72
+ multi_json (1.10.1)
73
+ netrc (0.7.7)
74
+ powerbar (1.0.11)
75
+ ansi (~> 1.4.0)
76
+ hashie (>= 1.1.0)
77
+ rack (1.4.5)
78
+ rack-cache (1.2)
79
+ rack (>= 0.4)
80
+ rack-ssl (1.3.4)
81
+ rack
82
+ rack-test (0.6.2)
83
+ rack (>= 1.0)
84
+ railties (3.2.19)
85
+ actionpack (= 3.2.19)
86
+ activesupport (= 3.2.19)
87
+ rack-ssl (~> 1.3.2)
88
+ rake (>= 0.8.7)
89
+ rdoc (~> 3.4)
90
+ thor (>= 0.14.6, < 2.0)
91
+ rake (10.3.2)
92
+ rdoc (3.12.2)
93
+ json (~> 1.4)
94
+ rest-client (1.7.2)
95
+ mime-types (>= 1.16, < 3.0)
96
+ netrc (~> 0.7)
97
+ shoulda (3.5.0)
98
+ shoulda-context (~> 1.0, >= 1.0.1)
99
+ shoulda-matchers (>= 1.4.1, < 3.0)
100
+ shoulda-context (1.2.1)
101
+ shoulda-matchers (2.7.0)
102
+ activesupport (>= 3.0.0)
103
+ simplecov (0.9.0)
104
+ docile (~> 1.1.0)
105
+ multi_json
106
+ simplecov-html (~> 0.8.0)
107
+ simplecov-html (0.8.0)
108
+ sprockets (2.2.2)
109
+ hike (~> 1.2)
110
+ multi_json (~> 1.0)
111
+ rack (~> 1.0)
112
+ tilt (~> 1.1, != 1.3.0)
113
+ sqlite3 (1.3.9)
114
+ term-ansicolor (1.3.0)
115
+ tins (~> 1.0)
116
+ thor (0.19.1)
117
+ tilt (1.4.1)
118
+ tins (1.3.2)
119
+ tzinfo (0.3.41)
120
+
121
+ PLATFORMS
122
+ ruby
123
+
124
+ DEPENDENCIES
125
+ actionpack (~> 3.2.0)
126
+ activerecord (~> 3.2.0)
127
+ appraisal
128
+ assert_difference
129
+ bundler
130
+ codeclimate-test-reporter
131
+ coveralls
132
+ minitest
133
+ minitest-rails
134
+ minitest-reporters
135
+ mocha
136
+ railties (~> 3.2.0)
137
+ rake
138
+ shoulda
139
+ simplecov
140
+ sqlite3
141
+ validation_auditor!
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 4.0.0"
6
+ gem "actionpack", "~> 4.0.0"
7
+ gem "railties", "~> 4.0.0"
8
+
9
+ gemspec :path => "../"
@@ -0,0 +1,125 @@
1
+ PATH
2
+ remote: ../
3
+ specs:
4
+ validation_auditor (1.0.0)
5
+ actionpack (>= 3.0.0)
6
+ activerecord (>= 3.0.0)
7
+ railties (>= 3.0.0)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ actionpack (4.0.9)
13
+ activesupport (= 4.0.9)
14
+ builder (~> 3.1.0)
15
+ erubis (~> 2.7.0)
16
+ rack (~> 1.5.2)
17
+ rack-test (~> 0.6.2)
18
+ activemodel (4.0.9)
19
+ activesupport (= 4.0.9)
20
+ builder (~> 3.1.0)
21
+ activerecord (4.0.9)
22
+ activemodel (= 4.0.9)
23
+ activerecord-deprecated_finders (~> 1.0.2)
24
+ activesupport (= 4.0.9)
25
+ arel (~> 4.0.0)
26
+ activerecord-deprecated_finders (1.0.3)
27
+ activesupport (4.0.9)
28
+ i18n (~> 0.6, >= 0.6.9)
29
+ minitest (~> 4.2)
30
+ multi_json (~> 1.3)
31
+ thread_safe (~> 0.1)
32
+ tzinfo (~> 0.3.37)
33
+ ansi (1.4.3)
34
+ appraisal (1.0.2)
35
+ bundler
36
+ rake
37
+ thor (>= 0.14.0)
38
+ arel (4.0.2)
39
+ assert_difference (0.5.0)
40
+ builder (3.1.4)
41
+ codeclimate-test-reporter (0.4.1)
42
+ simplecov (>= 0.7.1, < 1.0.0)
43
+ coveralls (0.7.1)
44
+ multi_json (~> 1.3)
45
+ rest-client
46
+ simplecov (>= 0.7)
47
+ term-ansicolor
48
+ thor
49
+ docile (1.1.5)
50
+ erubis (2.7.0)
51
+ hashie (3.3.1)
52
+ i18n (0.6.11)
53
+ metaclass (0.0.4)
54
+ mime-types (1.25.1)
55
+ minitest (4.7.5)
56
+ minitest-rails (1.0.1)
57
+ minitest (~> 4.7)
58
+ minitest-test (~> 1.0)
59
+ railties (>= 3.0, < 4.1)
60
+ minitest-reporters (0.14.24)
61
+ ansi
62
+ builder
63
+ minitest (>= 2.12, < 5.0)
64
+ powerbar
65
+ minitest-test (1.1.0)
66
+ minitest (~> 4.0)
67
+ mocha (1.1.0)
68
+ metaclass (~> 0.0.1)
69
+ multi_json (1.10.1)
70
+ netrc (0.7.7)
71
+ powerbar (1.0.11)
72
+ ansi (~> 1.4.0)
73
+ hashie (>= 1.1.0)
74
+ rack (1.5.2)
75
+ rack-test (0.6.2)
76
+ rack (>= 1.0)
77
+ railties (4.0.9)
78
+ actionpack (= 4.0.9)
79
+ activesupport (= 4.0.9)
80
+ rake (>= 0.8.7)
81
+ thor (>= 0.18.1, < 2.0)
82
+ rake (10.3.2)
83
+ rest-client (1.7.2)
84
+ mime-types (>= 1.16, < 3.0)
85
+ netrc (~> 0.7)
86
+ shoulda (3.5.0)
87
+ shoulda-context (~> 1.0, >= 1.0.1)
88
+ shoulda-matchers (>= 1.4.1, < 3.0)
89
+ shoulda-context (1.2.1)
90
+ shoulda-matchers (2.7.0)
91
+ activesupport (>= 3.0.0)
92
+ simplecov (0.9.0)
93
+ docile (~> 1.1.0)
94
+ multi_json
95
+ simplecov-html (~> 0.8.0)
96
+ simplecov-html (0.8.0)
97
+ sqlite3 (1.3.9)
98
+ term-ansicolor (1.3.0)
99
+ tins (~> 1.0)
100
+ thor (0.19.1)
101
+ thread_safe (0.3.4)
102
+ tins (1.3.2)
103
+ tzinfo (0.3.41)
104
+
105
+ PLATFORMS
106
+ ruby
107
+
108
+ DEPENDENCIES
109
+ actionpack (~> 4.0.0)
110
+ activerecord (~> 4.0.0)
111
+ appraisal
112
+ assert_difference
113
+ bundler
114
+ codeclimate-test-reporter
115
+ coveralls
116
+ minitest
117
+ minitest-rails
118
+ minitest-reporters
119
+ mocha
120
+ railties (~> 4.0.0)
121
+ rake
122
+ shoulda
123
+ simplecov
124
+ sqlite3
125
+ validation_auditor!
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 4.1.0"
6
+ gem "actionpack", "~> 4.1.0"
7
+ gem "railties", "~> 4.1.0"
8
+
9
+ gemspec :path => "../"
@@ -0,0 +1,122 @@
1
+ PATH
2
+ remote: ../
3
+ specs:
4
+ validation_auditor (1.0.0)
5
+ actionpack (>= 3.0.0)
6
+ activerecord (>= 3.0.0)
7
+ railties (>= 3.0.0)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ actionpack (4.1.5)
13
+ actionview (= 4.1.5)
14
+ activesupport (= 4.1.5)
15
+ rack (~> 1.5.2)
16
+ rack-test (~> 0.6.2)
17
+ actionview (4.1.5)
18
+ activesupport (= 4.1.5)
19
+ builder (~> 3.1)
20
+ erubis (~> 2.7.0)
21
+ activemodel (4.1.5)
22
+ activesupport (= 4.1.5)
23
+ builder (~> 3.1)
24
+ activerecord (4.1.5)
25
+ activemodel (= 4.1.5)
26
+ activesupport (= 4.1.5)
27
+ arel (~> 5.0.0)
28
+ activesupport (4.1.5)
29
+ i18n (~> 0.6, >= 0.6.9)
30
+ json (~> 1.7, >= 1.7.7)
31
+ minitest (~> 5.1)
32
+ thread_safe (~> 0.1)
33
+ tzinfo (~> 1.1)
34
+ ansi (1.4.3)
35
+ appraisal (1.0.2)
36
+ bundler
37
+ rake
38
+ thor (>= 0.14.0)
39
+ arel (5.0.1.20140414130214)
40
+ assert_difference (0.5.0)
41
+ builder (3.2.2)
42
+ codeclimate-test-reporter (0.4.1)
43
+ simplecov (>= 0.7.1, < 1.0.0)
44
+ coveralls (0.7.1)
45
+ multi_json (~> 1.3)
46
+ rest-client
47
+ simplecov (>= 0.7)
48
+ term-ansicolor
49
+ thor
50
+ docile (1.1.5)
51
+ erubis (2.7.0)
52
+ i18n (0.6.11)
53
+ json (1.8.1)
54
+ metaclass (0.0.4)
55
+ mime-types (1.25.1)
56
+ minitest (5.4.1)
57
+ minitest-rails (2.1.0)
58
+ minitest (~> 5.4)
59
+ railties (~> 4.1)
60
+ minitest-reporters (1.0.5)
61
+ ansi
62
+ builder
63
+ minitest (>= 5.0)
64
+ ruby-progressbar
65
+ mocha (1.1.0)
66
+ metaclass (~> 0.0.1)
67
+ multi_json (1.10.1)
68
+ netrc (0.7.7)
69
+ rack (1.5.2)
70
+ rack-test (0.6.2)
71
+ rack (>= 1.0)
72
+ railties (4.1.5)
73
+ actionpack (= 4.1.5)
74
+ activesupport (= 4.1.5)
75
+ rake (>= 0.8.7)
76
+ thor (>= 0.18.1, < 2.0)
77
+ rake (10.3.2)
78
+ rest-client (1.7.2)
79
+ mime-types (>= 1.16, < 3.0)
80
+ netrc (~> 0.7)
81
+ ruby-progressbar (1.5.1)
82
+ shoulda (3.5.0)
83
+ shoulda-context (~> 1.0, >= 1.0.1)
84
+ shoulda-matchers (>= 1.4.1, < 3.0)
85
+ shoulda-context (1.2.1)
86
+ shoulda-matchers (2.7.0)
87
+ activesupport (>= 3.0.0)
88
+ simplecov (0.9.0)
89
+ docile (~> 1.1.0)
90
+ multi_json
91
+ simplecov-html (~> 0.8.0)
92
+ simplecov-html (0.8.0)
93
+ sqlite3 (1.3.9)
94
+ term-ansicolor (1.3.0)
95
+ tins (~> 1.0)
96
+ thor (0.19.1)
97
+ thread_safe (0.3.4)
98
+ tins (1.3.2)
99
+ tzinfo (1.2.2)
100
+ thread_safe (~> 0.1)
101
+
102
+ PLATFORMS
103
+ ruby
104
+
105
+ DEPENDENCIES
106
+ actionpack (~> 4.1.0)
107
+ activerecord (~> 4.1.0)
108
+ appraisal
109
+ assert_difference
110
+ bundler
111
+ codeclimate-test-reporter
112
+ coveralls
113
+ minitest
114
+ minitest-rails
115
+ minitest-reporters
116
+ mocha
117
+ railties (~> 4.1.0)
118
+ rake
119
+ shoulda
120
+ simplecov
121
+ sqlite3
122
+ validation_auditor!
@@ -5,13 +5,14 @@ require "rails/generators"
5
5
  require "rails/generators/migration"
6
6
  require "rails/generators/active_record"
7
7
 
8
- # Extend the DelayedJobGenerator so that it creates an AR migration
9
8
  module ValidationAuditor
9
+ # Generator to be able to run `rails generate validation_auditor:install`
10
10
  class InstallGenerator < Rails::Generators::Base
11
11
  include Rails::Generators::Migration
12
12
 
13
13
  self.source_paths << File.join(File.dirname(__FILE__), "templates")
14
14
 
15
+ # Create migration files.
15
16
  def create_migration_file
16
17
  migration_template "migration.rb", "db/migrate/create_validation_audits.rb"
17
18
  end
@@ -1,4 +1,6 @@
1
+ # Create table to store validation errors.
1
2
  class CreateValidationAudits < ActiveRecord::Migration
3
+ # Create table to store validation errors.
2
4
  def up
3
5
  create_table :validation_audits do |t|
4
6
  t.integer :record_id
@@ -6,6 +6,9 @@ require "active_record"
6
6
  require "action_controller"
7
7
 
8
8
  module ValidationAuditor
9
+ mattr_accessor :exception_handler
10
+
11
+ # Validation audit model.
9
12
  class ValidationAudit < ActiveRecord::Base
10
13
  belongs_to :record, :polymorphic => true
11
14
 
@@ -14,33 +17,77 @@ module ValidationAuditor
14
17
  serialize :data, Hash
15
18
  serialize :params, Hash
16
19
 
17
-
18
20
  if respond_to? :attr_accessible # For Rails < 4 or Rails >= 4 with attr_accessible added by a third party library.
19
21
  begin
20
22
  attr_accessible
21
23
  rescue RuntimeError # Rails 4 raises a RuntimeError when you call attr_accessible, but we still want to call it in case you added attr_accessible gem.
22
24
  end
23
25
  end
26
+
27
+ # If available, add controller data to this validation audit. This will include:
28
+ # - params
29
+ # - url
30
+ # - user_agent
31
+ #
32
+ # @see ValidationAuditor::Controller::ClassMethods#audit_validation_errors
33
+ def add_controller
34
+ if ValidationAuditor::Controller.request.present?
35
+ request = ValidationAuditor::Controller.request
36
+ self.params = ValidationAuditor::Controller.clean_params(request.filtered_parameters)
37
+ self.url = request.url
38
+ self.user_agent = request.env["HTTP_USER_AGENT"]
39
+ end
40
+ end
24
41
  end
25
42
 
43
+ # Controller side of audit validations.
44
+ #
45
+ # @see ValidationAuditor::Controller::ClassMethods#audit_validation_errors
26
46
  module Controller
27
47
  extend ActiveSupport::Concern
28
48
 
49
+ # Class methods accessible in controllers (classes inheriting from ActionController::Base).
29
50
  module ClassMethods
51
+ # Enable validation audits for this controller. This is not essential to be able to audit validations, but it
52
+ # enhances the reports with:
53
+ # - params
54
+ # - url
55
+ # - user_agent
56
+ #
57
+ # For example:
58
+ #
59
+ # class BlogsController < ApplicationController
60
+ # audit_validation_errors
61
+ # end
62
+ #
63
+ # @see ValidationAuditor::Model::ClassMethods#audit_validation_errors
30
64
  def audit_validation_errors
31
65
  before_filter :make_request_auditable
32
66
  end
33
67
  end
34
68
 
69
+ private
70
+
71
+ # Make the request available for later adding some of its details to the validation audit. This method is
72
+ # automatically called as before_filter when you enable validation audits for a controller.
73
+ #
74
+ # @see ValidationAuditor::Controller::ClassMethods#audit_validation_errors
35
75
  def make_request_auditable
36
76
  Thread.current[:validation_auditor_request] = self.request
37
77
  end
38
78
 
79
+ # Getter for the request for the audit.
80
+ #
81
+ # @see ValidationAuditor::Controller::ClassMethods#audit_validation_errors
39
82
  def self.request
40
83
  Thread.current[:validation_auditor_request]
41
84
  end
42
85
 
43
86
  # Clean parameters before storing them in the database.
87
+ #
88
+ # This method gets sure we don't have unserializable values, like UploadedFiles.
89
+ # @param param [Hash] Hash of params as created by Rails
90
+ # @return [Hash] A hash without those problematic values.
44
91
  def self.clean_params(param)
45
92
  if param.is_a? Hash
46
93
  cleaned_params = {}
@@ -58,33 +105,50 @@ module ValidationAuditor
58
105
  end
59
106
  end
60
107
 
108
+ # Model side of audit validations.
109
+ #
110
+ # @see ValidationAuditor::Model::ClassMethods#audit_validation_errors
61
111
  module Model
62
112
  extend ActiveSupport::Concern
63
113
 
114
+ # Class methods accessible in models (classes inheriting from ActiveRecord::Base).
64
115
  module ClassMethods
116
+ # Enable validation audits for a model. For example:
117
+ #
118
+ # class Blog < ActiveRecord::Base
119
+ # audit_validation_errors
120
+ # end
121
+ #
122
+ # @see ValidationAuditor::Controller::ClassMethods#audit_validation_errors
65
123
  def audit_validation_errors
66
124
  after_rollback :audit_validation
67
125
  end
68
126
  end
69
127
 
128
+ private
129
+
130
+ # Perform the actual audit validation. This method is automatically called on rollback when you enable validation on
131
+ # a model.
132
+ #
133
+ # @see ValidationAuditor::Model::ClassMethods#audit_validation_errors
70
134
  def audit_validation
71
- if !errors.empty? # We don't use :valid? to avoid re-running validations
72
- va = ValidationAudit.new
73
- va.failures = self.errors.to_hash
74
- va.failure_messages = self.errors.full_messages.to_a
75
- va.data = self.attributes
76
- if self.new_record? # For new records
77
- va.record_type = self.class.name # we only store the class's name.
78
- else
79
- va.record = self
80
- end
81
- if ValidationAuditor::Controller.request.present?
82
- request = ValidationAuditor::Controller.request
83
- va.params = ValidationAuditor::Controller.clean_params(request.filtered_parameters)
84
- va.url = request.url
85
- va.user_agent = request.env["HTTP_USER_AGENT"]
86
- end
87
- va.save
135
+ return if errors.empty? # We don't use :valid? to avoid re-running validations
136
+ validatio_audit = ValidationAudit.new
137
+ validatio_audit.failures = self.errors.to_hash
138
+ validatio_audit.failure_messages = self.errors.full_messages.to_a
139
+ validatio_audit.data = self.attributes
140
+ if self.new_record? # For new records
141
+ validatio_audit.record_type = self.class.name # we only store the class's name.
142
+ else
143
+ validatio_audit.record = self
144
+ end
145
+ validatio_audit.add_controller
146
+ validatio_audit.save!
147
+ rescue Exception => e
148
+ if ValidationAuditor.exception_handler.nil?
149
+ raise # If there's no exception handler, just re-raise the exception.
150
+ else
151
+ ValidationAuditor.exception_handler.call(e, validatio_audit)
88
152
  end
89
153
  end
90
154
  end
@@ -2,5 +2,5 @@
2
2
  # Copyright © 2014, Watu
3
3
 
4
4
  module ValidationAuditor
5
- VERSION = "0.2.1"
5
+ VERSION = "1.0.0"
6
6
  end
data/test/model_test.rb CHANGED
@@ -2,6 +2,7 @@
2
2
  # Copyright © 2014, Watu
3
3
 
4
4
  require_relative "test_helper"
5
+ require "rails/version" # Required to access Rails::VERSION::MAJOR
5
6
 
6
7
  class ModelTest < ActiveSupport::TestCase
7
8
  should "not create a validation audit when no errors occurs" do
@@ -45,10 +46,52 @@ class ModelTest < ActiveSupport::TestCase
45
46
  end
46
47
  end
47
48
 
48
- should "not create a validation audit for non audited models" do
49
- assert_difference "ValidationAuditor::ValidationAudit.count" => 0 do
50
- NonAuditedRecord.create(name: "John Doe") # Missing email.
49
+ if Rails::VERSION::MAJOR >= 4 # Prior to Rails 4, it seems exceptions in after_rollback were silently swallowed: https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/6064-exceptions-from-after_commit-and-after_rollback-from-observers-are-quietly-swallowed
50
+ should "let exceptions propagate if no handler has been set up" do
51
+ ValidationAuditor::ValidationAudit.any_instance.expects(:save!).raises(Exception, "Something went wrong saving the audit")
52
+ assert_raises Exception do
53
+ AuditedRecord.create(name: "John Doe")
54
+ end
55
+ end
56
+ end
57
+
58
+ should "not let exceptions propagate when a handler has been set up" do
59
+ exception_message = "Something went wrong saving the audit"
60
+ exception_handler_called = false
61
+ ValidationAuditor.exception_handler = lambda do |e, va|
62
+ assert e.is_a?(Exception)
63
+ assert_equal e.message, exception_message
64
+ assert va.is_a?(ValidationAuditor::ValidationAudit)
65
+ exception_handler_called = true
66
+ end
67
+ ValidationAuditor::ValidationAudit.any_instance.stubs(:save!).raises(Exception, exception_message)
68
+
69
+ AuditedRecord.create(name: "John Doe")
70
+
71
+ assert exception_handler_called
72
+
73
+ # teardown
74
+ ValidationAuditor.exception_handler = nil
75
+ end
76
+
77
+ should "add controller information if possible" do
78
+ params = {"hello" => "world", "universe" => "42"}
79
+ url = "https://example.com/hello/world"
80
+ env = {"HTTP_USER_AGENT" => "NCSA_Mosaic/2.0"}
81
+ request = mock("request") do
82
+ stubs(:present?).returns(true)
83
+ stubs(:filtered_parameters).returns(params)
84
+ stubs(:url).returns(url)
85
+ stubs(:env).returns(env)
51
86
  end
87
+ ValidationAuditor::Controller.stubs(:request).returns(request)
88
+ assert_difference "ValidationAuditor::ValidationAudit.count" => +1 do
89
+ AuditedRecord.create(name: "John Doe") # Missing email.
90
+ end
91
+ audit = ValidationAuditor::ValidationAudit.order(:id).last
92
+ assert_equal params, audit.params
93
+ assert_equal url, audit.url
94
+ assert_equal env["HTTP_USER_AGENT"], audit.user_agent
52
95
  end
53
96
  end
54
97
 
data/test/test_helper.rb CHANGED
@@ -10,15 +10,15 @@ SimpleCov.start do
10
10
  add_filter "/test/"
11
11
  end
12
12
  Coveralls.wear! # Comment out this line to have the local coverage generated.
13
+ require "codeclimate-test-reporter"
14
+ CodeClimate::TestReporter.start
13
15
 
14
16
  require "minitest/autorun"
15
17
  require "minitest/reporters"
16
18
  MiniTest::Reporters.use!
17
-
18
19
  require "active_support/test_case"
19
20
  require "action_controller"
20
21
  require "action_controller/test_case"
21
-
22
22
  require "shoulda"
23
23
  require "shoulda-context"
24
24
  require "shoulda-matchers"
@@ -33,7 +33,7 @@ require "logger"
33
33
  ActiveRecord::Base.logger = Logger.new(STDERR)
34
34
  ActiveRecord::Base.logger.level = Logger::WARN
35
35
  ActiveRecord::Base.configurations = {"sqlite3" => {adapter: "sqlite3", database: ":memory:"}}
36
- ActiveRecord::Base.establish_connection("sqlite3")
36
+ ActiveRecord::Base.establish_connection(:sqlite3)
37
37
 
38
38
  # Models setup.
39
39
  require "validation_auditor"
@@ -21,7 +21,21 @@ Gem::Specification.new do |spec|
21
21
  spec.require_paths = ["lib"]
22
22
 
23
23
  spec.required_ruby_version = ">= 1.9.3"
24
- spec.add_dependency "activerecord", "> 3.2.0"
25
- spec.add_dependency "actionpack", "> 3.2.0"
26
- spec.add_dependency "railties", "> 3.2.0"
24
+ spec.add_dependency "activerecord", ">= 3.0.0"
25
+ spec.add_dependency "actionpack", ">= 3.0.0"
26
+ spec.add_dependency "railties", ">= 3.0.0"
27
+
28
+ spec.add_development_dependency "appraisal"
29
+ spec.add_development_dependency "assert_difference"
30
+ spec.add_development_dependency "bundler"
31
+ spec.add_development_dependency "codeclimate-test-reporter"
32
+ spec.add_development_dependency "coveralls" #, require: false
33
+ spec.add_development_dependency "minitest"
34
+ spec.add_development_dependency "minitest-reporters"
35
+ spec.add_development_dependency "minitest-rails"
36
+ spec.add_development_dependency "mocha"
37
+ spec.add_development_dependency "rake"
38
+ spec.add_development_dependency "simplecov"
39
+ spec.add_development_dependency "shoulda"
40
+ spec.add_development_dependency "sqlite3"
27
41
  end
metadata CHANGED
@@ -1,57 +1,239 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: validation_auditor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - J. Pablo Fernández
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-05 00:00:00.000000000 Z
11
+ date: 2014-11-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 3.2.0
19
+ version: 3.0.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 3.2.0
26
+ version: 3.0.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: actionpack
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 3.2.0
33
+ version: 3.0.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: 3.2.0
40
+ version: 3.0.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: railties
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: 3.2.0
47
+ version: 3.0.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: 3.2.0
54
+ version: 3.0.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: appraisal
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: assert_difference
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: codeclimate-test-reporter
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: coveralls
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: minitest
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: minitest-reporters
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: minitest-rails
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: mocha
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: rake
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: simplecov
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
209
+ - !ruby/object:Gem::Dependency
210
+ name: shoulda
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ version: '0'
216
+ type: :development
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: '0'
223
+ - !ruby/object:Gem::Dependency
224
+ name: sqlite3
225
+ requirement: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - ">="
228
+ - !ruby/object:Gem::Version
229
+ version: '0'
230
+ type: :development
231
+ prerelease: false
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - ">="
235
+ - !ruby/object:Gem::Version
236
+ version: '0'
55
237
  description: Log validation errors to the database for later inspection.
56
238
  email:
57
239
  - pupeno@watuapp.com
@@ -61,14 +243,18 @@ extra_rdoc_files: []
61
243
  files:
62
244
  - ".gitignore"
63
245
  - ".travis.yml"
246
+ - Appraisals
64
247
  - CHANGELOG.md
65
248
  - Gemfile
66
249
  - LICENSE.txt
67
250
  - README.md
68
251
  - Rakefile
69
- - gemfiles/rails_3_2.Gemfile
70
- - gemfiles/rails_4_0.Gemfile
71
- - gemfiles/rails_4_1.Gemfile
252
+ - gemfiles/rails_3_2.gemfile
253
+ - gemfiles/rails_3_2.gemfile.lock
254
+ - gemfiles/rails_4_0.gemfile
255
+ - gemfiles/rails_4_0.gemfile.lock
256
+ - gemfiles/rails_4_1.gemfile
257
+ - gemfiles/rails_4_1.gemfile.lock
72
258
  - lib/generators/validation_auditor/install_generator.rb
73
259
  - lib/generators/validation_auditor/templates/migration.rb
74
260
  - lib/validation_auditor.rb
@@ -98,7 +284,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
98
284
  version: '0'
99
285
  requirements: []
100
286
  rubyforge_project:
101
- rubygems_version: 2.2.2
287
+ rubygems_version: 2.4.3
102
288
  signing_key:
103
289
  specification_version: 4
104
290
  summary: Log validation errors to the database for later inspection.
@@ -1,18 +0,0 @@
1
- # encoding: UTF-8
2
- # Copyright © 2013, 2014, Watu
3
-
4
- source "https://rubygems.org"
5
-
6
- gem "assert_difference"
7
- gem "bundler", "~> 1.3"
8
- gem "coveralls", require: false
9
- gem "minitest"
10
- gem "minitest-reporters"
11
- gem "minitest-rails"
12
- gem "rake"
13
- gem "simplecov"
14
- gem "shoulda"
15
- gem "sqlite3"
16
- gem "activerecord", "~> 3.2.0"
17
- gem "actionpack", "~> 3.2.0"
18
- gem "railties", "~> 3.2.0"
@@ -1,18 +0,0 @@
1
- # encoding: UTF-8
2
- # Copyright © 2014, Watu
3
-
4
- source "https://rubygems.org"
5
-
6
- gem "assert_difference"
7
- gem "bundler", "~> 1.3"
8
- gem "coveralls", require: false
9
- gem "minitest"
10
- gem "minitest-reporters"
11
- gem "minitest-rails"
12
- gem "rake"
13
- gem "simplecov"
14
- gem "shoulda"
15
- gem "sqlite3"
16
- gem "activerecord", "~> 4.0.0"
17
- gem "actionpack", "~> 4.0.0"
18
- gem "railties", "~> 4.0.0"
@@ -1,18 +0,0 @@
1
- # encoding: UTF-8
2
- # Copyright © 2014, Watu
3
-
4
- source "https://rubygems.org"
5
-
6
- gem "assert_difference"
7
- gem "bundler", "~> 1.3"
8
- gem "coveralls", require: false
9
- gem "minitest"
10
- gem "minitest-reporters"
11
- gem "minitest-rails"
12
- gem "rake"
13
- gem "simplecov"
14
- gem "shoulda"
15
- gem "sqlite3"
16
- gem "activerecord", "~> 4.1.0"
17
- gem "actionpack", "~> 4.1.0"
18
- gem "railties", "~> 4.1.0"