validation_issues 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +50 -0
  3. data/Rakefile +40 -0
  4. data/app/assets/javascripts/validation_issues.js +2 -0
  5. data/app/assets/stylesheets/scaffold.css +56 -0
  6. data/app/assets/stylesheets/validation_issues.css +5 -0
  7. data/app/assets/stylesheets/validation_issues/buttons.css +77 -0
  8. data/app/controllers/validation_admin/validation_issues_controller.rb +44 -0
  9. data/app/models/validation_issue.rb +26 -0
  10. data/app/views/validation_admin/validation_issues/index.html.erb +25 -0
  11. data/app/views/validation_admin/validation_issues/show.html.erb +22 -0
  12. data/config/routes.rb +5 -0
  13. data/db/migrate/20120722204204_create_validation_issues.rb +16 -0
  14. data/lib/generators/valid_issues/templates/migration.rb +22 -0
  15. data/lib/generators/valid_issues/valid_issues_generator.rb +23 -0
  16. data/lib/tasks/validation_issues_tasks.rake +4 -0
  17. data/lib/validation_issues.rb +7 -0
  18. data/lib/validation_issues/acts_as_validation_issues.rb +37 -0
  19. data/lib/validation_issues/core_ext.rb +0 -0
  20. data/lib/validation_issues/engine.rb +4 -0
  21. data/lib/validation_issues/version.rb +3 -0
  22. data/test/acts_as_validation_issues_test.rb +21 -0
  23. data/test/dummy/README.rdoc +261 -0
  24. data/test/dummy/Rakefile +7 -0
  25. data/test/dummy/app/assets/javascripts/application.js +15 -0
  26. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  27. data/test/dummy/app/controllers/application_controller.rb +3 -0
  28. data/test/dummy/app/helpers/application_helper.rb +2 -0
  29. data/test/dummy/app/models/user.rb +10 -0
  30. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  31. data/test/dummy/config.ru +4 -0
  32. data/test/dummy/config/application.rb +56 -0
  33. data/test/dummy/config/boot.rb +10 -0
  34. data/test/dummy/config/database.yml +25 -0
  35. data/test/dummy/config/environment.rb +5 -0
  36. data/test/dummy/config/environments/development.rb +37 -0
  37. data/test/dummy/config/environments/production.rb +67 -0
  38. data/test/dummy/config/environments/test.rb +37 -0
  39. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  40. data/test/dummy/config/initializers/inflections.rb +15 -0
  41. data/test/dummy/config/initializers/mime_types.rb +5 -0
  42. data/test/dummy/config/initializers/secret_token.rb +7 -0
  43. data/test/dummy/config/initializers/session_store.rb +8 -0
  44. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  45. data/test/dummy/config/locales/en.yml +5 -0
  46. data/test/dummy/config/routes.rb +58 -0
  47. data/test/dummy/db/development.sqlite3 +0 -0
  48. data/test/dummy/db/migrate/20120722183345_create_users.rb +11 -0
  49. data/test/dummy/db/schema.rb +38 -0
  50. data/test/dummy/db/test.sqlite3 +0 -0
  51. data/test/dummy/log/development.log +128 -0
  52. data/test/dummy/log/test.log +1129 -0
  53. data/test/dummy/public/404.html +26 -0
  54. data/test/dummy/public/422.html +26 -0
  55. data/test/dummy/public/500.html +25 -0
  56. data/test/dummy/public/favicon.ico +0 -0
  57. data/test/dummy/script/rails +6 -0
  58. data/test/dummy/test/fixtures/users.yml +11 -0
  59. data/test/dummy/test/unit/user_test.rb +7 -0
  60. data/test/factories/validation_issues.rb +9 -0
  61. data/test/functional/validation_issues_controller_test.rb +42 -0
  62. data/test/integration/navigation_test.rb +10 -0
  63. data/test/test_helper.rb +17 -0
  64. data/test/unit/helpers/validation_issues_helper_test.rb +4 -0
  65. data/test/unit/validation_issue_test.rb +23 -0
  66. data/test/validation_issues_test.rb +7 -0
  67. metadata +186 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2012 YOURNAME
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.rdoc ADDED
@@ -0,0 +1,50 @@
1
+ = ValidationIssues
2
+
3
+ The Reason for this project is simple. People are paying attention to A/B tests and user conversion without having all the details.
4
+
5
+ For example, Lets say you have a form that appears to have users drop out of your signup funnel. Normally the first stab at the problem is to create an A/B test without any data.
6
+
7
+ What if you knew 90% of the people that hit the form had a validation error on the first_name field. I'm betting that you might not need an AB test. If you did add an AB test you would focus on the first_name field.
8
+
9
+ This problem actually happened on a live production app I was working on. Turns out the first_name validated white space. If a user added a space at the end of their name the validation would fail. The user would receive a "Invalid First Name" message. They would look at their name and be confused and eventually leave. The solution was as simple as sanitizing the first_name before validations. If we had this tool the problem would have never lasted for so long.
10
+
11
+ = Getting started
12
+
13
+ add this to your gemfile
14
+
15
+ gem 'validation_issues'
16
+
17
+ In your application_controller.rb add the following to have permissions to see the admin section:
18
+
19
+ class ApplicationController < ActionController::Base
20
+ protected
21
+ def has_validation_issues_admin_privileges
22
+ # logic you want to make this true
23
+ # Typically this will work
24
+ current_user && current_user.admin?
25
+ end
26
+ end
27
+
28
+ In your model add 'acts_as_validation_issues'
29
+
30
+ class User < ActiveRecord::Base
31
+ # add this line
32
+ acts_as_validation_issues
33
+ end
34
+
35
+ Now the only you need to do is this in your controller:
36
+
37
+ def create
38
+ user = User.new(:first_name => '', :last_name => 'Good Last Name')
39
+ if user.save
40
+ # do something
41
+ else
42
+ user.log_validation_issue!('user_form')
43
+ # do something else(like render the form again)
44
+ end
45
+ end
46
+
47
+ you pass the form's name into the user.log_validation_issue! method. This way if you have an AB test you can see the difference in the results.
48
+
49
+ Happy Coding. If you have any problems feel free to ask me directly, David Henner
50
+
data/Rakefile ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'ValidationIssues'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+
27
+
28
+ Bundler::GemHelper.install_tasks
29
+
30
+ require 'rake/testtask'
31
+
32
+ Rake::TestTask.new(:test) do |t|
33
+ t.libs << 'lib'
34
+ t.libs << 'test'
35
+ t.pattern = 'test/**/*_test.rb'
36
+ t.verbose = false
37
+ end
38
+
39
+
40
+ task :default => :test
@@ -0,0 +1,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -0,0 +1,56 @@
1
+ body { background-color: #fff; color: #333; }
2
+
3
+ body, p, ol, ul, td {
4
+ font-family: verdana, arial, helvetica, sans-serif;
5
+ font-size: 13px;
6
+ line-height: 18px;
7
+ }
8
+
9
+ pre {
10
+ background-color: #eee;
11
+ padding: 10px;
12
+ font-size: 11px;
13
+ }
14
+
15
+ a { color: #000; }
16
+ a:visited { color: #666; }
17
+ a:hover { color: #fff; background-color:#000; }
18
+
19
+ div.field, div.actions {
20
+ margin-bottom: 10px;
21
+ }
22
+
23
+ #notice {
24
+ color: green;
25
+ }
26
+
27
+ .field_with_errors {
28
+ padding: 2px;
29
+ background-color: red;
30
+ display: table;
31
+ }
32
+
33
+ #error_explanation {
34
+ width: 450px;
35
+ border: 2px solid red;
36
+ padding: 7px;
37
+ padding-bottom: 0;
38
+ margin-bottom: 20px;
39
+ background-color: #f0f0f0;
40
+ }
41
+
42
+ #error_explanation h2 {
43
+ text-align: left;
44
+ font-weight: bold;
45
+ padding: 5px 5px 5px 15px;
46
+ font-size: 12px;
47
+ margin: -7px;
48
+ margin-bottom: 0px;
49
+ background-color: #c00;
50
+ color: #fff;
51
+ }
52
+
53
+ #error_explanation ul li {
54
+ font-size: 12px;
55
+ list-style: square;
56
+ }
@@ -0,0 +1,5 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
5
+ //= require_tree ./validation_issues
@@ -0,0 +1,77 @@
1
+ .button {
2
+ display: inline-block;
3
+ zoom: 1; /* zoom and *display = ie7 hack for display:inline-block */
4
+ *display: inline;
5
+ vertical-align: baseline;
6
+ margin: 0 2px;
7
+ outline: none;
8
+ cursor: pointer;
9
+ text-align: center;
10
+ text-decoration: none;
11
+ font: 14px/100% Arial, Helvetica, sans-serif;
12
+ padding: .5em 2em .55em;
13
+ text-shadow: 0 1px 1px rgba(0,0,0,.3);
14
+ -webkit-border-radius: .25em;
15
+ -moz-border-radius: .25em;
16
+ border-radius: .25em;
17
+ -webkit-box-shadow: 0 1px 1px rgba(0,0,0,.2);
18
+ -moz-box-shadow: 0 1px 1px rgba(0,0,0,.2);
19
+ box-shadow: 0 1px 1px rgba(0,0,0,.2);
20
+ }
21
+
22
+ input[type="submit"].button {border: 0;background-color: transparent;color: #888;}
23
+ input[type="submit"].button.dark-orange-button {color:#FFF;}
24
+ input[type="submit"].button.small-dark-orange-button {color:#FFF;}
25
+
26
+ .dark-orange-button {
27
+ color: #fef4e9;
28
+ border: solid 1px #db3909;
29
+ background: #c6481a;
30
+ background: -webkit-gradient(linear, left top, left bottom, from(#c8581a), to(#c5441F));
31
+ background: -moz-linear-gradient(top, #c8581a, #c5441F);
32
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#c8581a', endColorstr='#c5441F');
33
+ }
34
+ .dark-orange-button:hover {
35
+ background: #c4421F;
36
+ background: -webkit-gradient(linear, left top, left bottom, from(#c75114), to(#c23415));
37
+ background: -moz-linear-gradient(top, #c75114, #c23415);
38
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#c75114', endColorstr='#c23415');
39
+ }
40
+ .dark-orange-button:active {
41
+ background: #c4421F;
42
+ background: -webkit-gradient(linear, left top, left bottom, from(#c75114), to(#c23415));
43
+ background: -moz-linear-gradient(top, #c75114, #c23415);
44
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#c75114', endColorstr='#c23415');
45
+ }
46
+
47
+ .small-dark-orange-button {
48
+ font-family:'Century Schoolbook Italic', 'Century Schoolbook', Georgia;
49
+ color: #fef4e9 !important;
50
+ border: solid 1px #db3909;
51
+ background: #c6481a;
52
+ background: -webkit-gradient(linear, left top, left bottom, from(#c9681a), to(#c6451F));
53
+ background: -moz-linear-gradient(top, #c9681a, #c6451F);
54
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#c9681a', endColorstr='#c6451F');
55
+ font-size: 14px;
56
+ font-weight: normal;
57
+ padding: 0.2em 0.7em;
58
+ &.myriad {
59
+ font-family: "MyriadPro","Lucida Sans Unicode", "Lucida Grande", sans-serif;
60
+ padding: 0.7em;
61
+ padding-top: 0.3em;
62
+ padding-bottom: 0.1em;
63
+ }
64
+ }
65
+ .small-dark-orange-button:hover {
66
+ color: #fff7ef;
67
+ background: #c4421F;
68
+ background: -webkit-gradient(linear, left top, left bottom, from(#c65114), to(#c23415));
69
+ background: -moz-linear-gradient(top, #c65114, #c23415);
70
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#c65114', endColorstr='#c23415');
71
+ }
72
+ .small-dark-orange-button:active {
73
+ background: #c4421F;
74
+ background: -webkit-gradient(linear, left top, left bottom, from(#c65114), to(#c23415));
75
+ background: -moz-linear-gradient(top, #c65114, #c23415);
76
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#c65114', endColorstr='#c23415');
77
+ }
@@ -0,0 +1,44 @@
1
+ class ValidationAdmin::ValidationIssuesController < ApplicationController
2
+ before_filter :redirect_if_no_privileges
3
+ # has_validation_issues_admin_privileges
4
+
5
+ # GET /validation_issues
6
+ # GET /validation_issues.json
7
+ def index
8
+ @validation_issues = ValidationIssue.all
9
+
10
+ respond_to do |format|
11
+ format.html # index.html.erb
12
+ format.json { render json: @validation_issues }
13
+ end
14
+ end
15
+
16
+ # GET /validation_issues/1
17
+ # GET /validation_issues/1.json
18
+ def show
19
+ @validation_issue = ValidationIssue.find(params[:id])
20
+
21
+ respond_to do |format|
22
+ format.html # show.html.erb
23
+ format.json { render json: @validation_issue }
24
+ end
25
+ end
26
+
27
+ # DELETE /validation_issues/1
28
+ # DELETE /validation_issues/1.json
29
+ def destroy
30
+ @validation_issue = ValidationIssue.find(params[:id])
31
+ @validation_issue.clear_data!
32
+
33
+ respond_to do |format|
34
+ format.html { redirect_to validation_admin_validation_issues_url }
35
+ format.json { head :no_content }
36
+ end
37
+ end
38
+
39
+ protected
40
+
41
+ def redirect_if_no_privileges
42
+ redirect_to root_url and return unless has_validation_issues_admin_privileges
43
+ end
44
+ end
@@ -0,0 +1,26 @@
1
+ class ValidationIssue < ActiveRecord::Base
2
+ attr_accessible :form_name, :issue_count, :issue_hash, :issue_type, :notes
3
+ serialize :issue_hash, Hash
4
+
5
+ validates :form_name, :presence => true, :uniqueness => true
6
+
7
+ def clear_data!
8
+ self.issue_count = 0
9
+ self.issue_hash = {}
10
+ self.notes = ''
11
+ self.save!
12
+ end
13
+
14
+ def increment_issue_count!
15
+ increment_with_sql!('issue_count')
16
+ end
17
+
18
+ protected
19
+
20
+ def increment_with_sql!(attribute, by = 1)
21
+ raise ArgumentError("Invalid attribute: #{attribute}") unless attribute_names.include?(attribute.to_s)
22
+ original_value_sql = "CASE WHEN `#{attribute}` IS NULL THEN 0 ELSE `#{attribute}` END"
23
+ self.class.update_all("`#{attribute}` = #{original_value_sql} + #{by.to_i}", "id = #{id}")
24
+ reload
25
+ end
26
+ end
@@ -0,0 +1,25 @@
1
+ <p id="notice"><%= notice %></p>
2
+
3
+ <h1> Validation Issues </h1>
4
+
5
+ <table>
6
+ <tr>
7
+ <th>Type</th>
8
+ <th>Form Name</th>
9
+ <th>Count</th>
10
+ <th></th>
11
+ <th></th>
12
+ </tr>
13
+
14
+ <% @validation_issues.each do |validation_issue| %>
15
+ <tr>
16
+ <td><%= validation_issue.issue_type %></td>
17
+ <td><%= validation_issue.form_name %></td>
18
+ <td><%= validation_issue.issue_count %></td>
19
+ <td><%= link_to 'Show', validation_admin_validation_issue_path(validation_issue) %></td>
20
+ <td><%= link_to 'Reset!',
21
+ validation_admin_validation_issue_path(validation_issue),
22
+ method: :delete, data: { confirm: 'Are you sure?' } %></td>
23
+ </tr>
24
+ <% end %>
25
+ </table>
@@ -0,0 +1,22 @@
1
+ <p id="notice"><%= notice %></p>
2
+
3
+ <h2> <%= @validation_issue.issue_type %>: <%= @validation_issue.form_name %> </h2>
4
+
5
+ <p>
6
+ <b>Issues (<%= @validation_issue.issue_count %>):</b>
7
+ <ul>
8
+ <% @validation_issue.issue_hash.each do |k, v| %>
9
+ <li><label> <%= k %>: </label> <%= v %></li>
10
+ <% end %>
11
+ </ul>
12
+
13
+ </p>
14
+
15
+ <% unless @validation_issue.notes.blank? %>
16
+ <p>
17
+ <b>Notes:</b>
18
+ <%= @validation_issue.notes %>
19
+ </p>
20
+ <% end %>
21
+
22
+ <%= link_to 'Back', validation_admin_validation_issues_path, :class => 'button small-dark-orange-button' %>
data/config/routes.rb ADDED
@@ -0,0 +1,5 @@
1
+ Rails.application.routes.draw do
2
+ namespace :validation_admin do
3
+ resources :validation_issues
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ class CreateValidationIssues < ActiveRecord::Migration
2
+ def change
3
+ create_table :validation_issues do |t|
4
+ t.string :issue_type
5
+ t.string :form_name, :null => false
6
+ t.text :issue_hash, :null => false
7
+ t.text :notes, :default => ''
8
+ t.integer :issue_count
9
+
10
+ t.timestamps
11
+ end
12
+ add_index :validation_issues, :form_name# , :length => 10
13
+ add_index :validation_issues, :issue_type
14
+ add_index :validation_issues, [:form_name, :issue_type], :unique => true
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ class CreateValidationIssues < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :validation_issues do |t|
4
+
5
+ t.string :issue_type, :null => false
6
+ t.string :form_name, :null => false
7
+ t.text :issue_hash, :null => false
8
+ #t.text :issued_by_ids
9
+ t.text :notes
10
+ t.integer :issue_count
11
+
12
+ t.timestamps
13
+ end
14
+ add_index :validation_issues, :form_name
15
+ add_index :validation_issues, :issue_type
16
+
17
+ end
18
+
19
+ def self.down
20
+ drop_table :versions
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ # CURRENT FILE :: lib/generators/valid_issues/valid_issues_generator.rb
2
+ # Requires
3
+ require 'rails/generators'
4
+ require 'rails/generators/migration'
5
+
6
+ class ValidIssuesGenerator < Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+ def self.source_root
9
+ @source_root ||= File.join(File.dirname(__FILE__), 'templates')
10
+ end
11
+
12
+ def self.next_migration_number(dirname)
13
+ if ActiveRecord::Base.timestamped_migrations
14
+ Time.new.utc.strftime("%Y%m%d%H%M%S")
15
+ else
16
+ "%.3d" % (current_migration_number(dirname) + 1)
17
+ end
18
+ end
19
+
20
+ def create_migration_file
21
+ migration_template 'migration.rb', 'db/migrate/create_validation_issues_table.rb'
22
+ end
23
+ end