validation_rage 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
4
+ - jruby
5
+ - rbx-19mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in validation_rage.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Michael Bumann
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # ValidationRage
2
+
3
+ ValidationRage is a gem to capture validation errors from your Ruby application.
4
+ The goal is to identify usability issues in your application that are caused by validations.
5
+
6
+ Imaging a form with several field validations. ValidationRage makes it super easy to get information on what fields the users experience validation errors (and might leave because of that).
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'validation_rage'
13
+
14
+
15
+ ## Usage
16
+
17
+ By default ValidationRage will hook into ActiveRecord::Base and log all validation errors to your Rails log.
18
+
19
+ ValidationRage comes with an rails engine to configure your application. "config" in the following examples refer to Rails.application.config
20
+ You can configure if you want to hook into your models or controllers.
21
+
22
+ ### Logging on model level ###
23
+
24
+ You can hook ValidationRage into your classes that are ActiveModel::Validation compatible.
25
+
26
+ example configuration:
27
+
28
+ config.validation_rage.attach_to = [User, Account, Session] # hook into the User, Account, Session classes and attach an after_validation callback to track invalid records
29
+
30
+ ### Logging on controller level ###
31
+
32
+ The problem with attaching ValidationRage to your models is that you do not have any information about the context: where did the error happen? what parameters causes the error?
33
+ That's why you can hook ValidationRage into your controllers. ValidationRage will add an after_callback checking your instance variables for errors.
34
+
35
+ example configuration:
36
+
37
+ config.validation_rage.attach_to = ["users#create", "session#create"] # hook into the Users and Session controler create actions
38
+
39
+ # using * to match any action or any controller:
40
+
41
+ config.validation_rage.attach_to = ["*#create"] # hook into every create action. - this is done by adding the after_filter to the ApplicationController
42
+ config.validation_rage.attach_to = ["users#*"] # hook into every action in the users controler.
43
+
44
+ example configuration with both model and controller level logging:
45
+
46
+ config.validation_rage.attach_to = ["users#create", Company] # hook into the create action of the UsersController AND the Company model
47
+
48
+
49
+ ### Using different notifiers ###
50
+
51
+ ValidationRage uses ActiveSupport::Notifications to publish a notification message when an validation error happened. This makes it super easy to write a listener to those events and process the validation error information.
52
+ This gem comes with different Notifiers and since the integrate with ActiveSupport::Notifiers you can use several notifiers at the same time and write your own notifer.
53
+
54
+ #### LogNotifier ####
55
+
56
+ uses the logger to log your validation errors.
57
+
58
+ configuration:
59
+
60
+ config.validation_rage.notifier["Log"] = {:log_level => :warn, :logger => Logger.new("log/validations.log")}
61
+
62
+ #### UdpNotifier ####
63
+
64
+ send the errors as JSON to a UDP server server
65
+
66
+ configuration:
67
+
68
+ config.validation_rage.notifier["Udp"] = {:host => "localhost", :port => 3333}
69
+
70
+
71
+ #### ValidationRageNotifer ####
72
+
73
+ send the errors to the ValidationRage Application which gives you a nice accessible interface to analyze your data.
74
+ Signup at validationrage.com to get your API key
75
+
76
+ configuration:
77
+
78
+ config.validation_rage.notifier["ValidationRage"] = {:api_key => "<YOUR API KEY>"}
79
+
80
+
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << 'test'
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+ task :default => :test
@@ -0,0 +1,18 @@
1
+ module ValidationRage
2
+ class BaseNotifier
3
+ class NotImplementedError < StandardError; end
4
+
5
+ def initialize(args={})
6
+ end
7
+ def call(event_name, payload)
8
+ raise NotImplementedError.new("your notifer must implement a call(event_name, payload) method")
9
+ end
10
+ def subscribe!
11
+ ActiveSupport::Notifications.subscribe(/validation_rage:.*/, self)
12
+ end
13
+
14
+ def data_present?(payload)
15
+ payload.values.first && !payload.values.first.empty?
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,28 @@
1
+ module ValidationRage
2
+ module ControllerExtension
3
+
4
+ def notify_validation_rage
5
+ objects_with_errors = self.instance_variables.collect {|var_name| instance_variable_get(var_name) if validation_rage_errors_present?(var_name) }.compact
6
+ objects_with_errors.each do |o|
7
+ ActiveSupport::Notifications.publish("validation_rage:errors", {o.class.name => o.errors.to_hash, :context => self.validation_rage_context })
8
+ end
9
+ end
10
+
11
+ def validation_rage_errors_present?(var_name)
12
+ return false if var_name.to_s =~ /^@_/ # ignore variable names marked with "_" - those are by convention internal only
13
+ obj = instance_variable_get(var_name)
14
+ obj.respond_to?(:errors) && !obj.errors.empty?
15
+ end
16
+
17
+ def validation_rage_context
18
+ {
19
+ :controller => params[:controller],
20
+ :action => params[:action],
21
+ :request_path => request.path,
22
+ :params => request.filtered_parameters,
23
+ :request_uuid => request.uuid,
24
+ :referrer => request.referrer
25
+ }
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,34 @@
1
+ require "fnordmetric"
2
+ module ValidationRage
3
+ class FnordMetricNotifier < BaseNotifier
4
+ attr_accessor :fnord
5
+
6
+ def initialize(args={})
7
+ self.fnord = FnordMetric::API.new(:redis_url => args[:redis_url])
8
+ end
9
+
10
+ # I guess this is toooooo sloooow but anyhow let's play with it
11
+ def call(event_name, payload)
12
+ return unless data_present?(payload)
13
+
14
+ # global validation error event
15
+ self.fnord.event({
16
+ :_type => event_name,
17
+ :payload => payload
18
+ })
19
+ # class level validation error event
20
+ self.fnord.event({
21
+ :_type => "validation_rage_error.#{payload.keys.first.to_s.downcase}",
22
+ :payload => payload.values.first.keys
23
+ })
24
+ # two events are enough for now
25
+ ## attribute level validation error event
26
+ #payload.values.first.each do |attribute, error_messages|
27
+ # self.fnord.event({
28
+ # :_type => "validation_rage_error.#{payload.keys.first.to_s.downcase}.#{attribute}",
29
+ # :payload => error_messages
30
+ # })
31
+ #end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,14 @@
1
+ module ValidationRage
2
+ class LogNotifier < BaseNotifier
3
+ attr_accessor :logger, :log_level
4
+ def initialize(args)
5
+ self.logger = args[:logger]
6
+ self.log_level = args[:log_level] || :warn
7
+ end
8
+ # TODO: optimize log format
9
+ def call(event_name, payload)
10
+ return if payload.values.first && payload.values.first.empty?
11
+ self.logger.send(self.log_level, "#{event_name} #{payload}")
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ module ValidationRage
2
+ module ModelExtension
3
+
4
+ def self.included(base)
5
+ base.after_validation(:notify_validation_rage)
6
+ end
7
+
8
+ def notify_validation_rage(context = {})
9
+ ActiveSupport::Notifications.publish("validation_rage:errors", {self.class.name => self.errors.to_hash, :context => context})
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,43 @@
1
+ module ValidationRage
2
+ class Engine < ::Rails::Engine
3
+
4
+ config.validation_rage = ActiveSupport::OrderedOptions.new
5
+
6
+ config.validation_rage.attach_to = []
7
+ config.validation_rage.attach_to = [ActiveRecord::Base] if defined?(ActiveRecord::Base) # really?!
8
+
9
+ config.validation_rage.notifier = {"Log" => {:log_level => :warn, :logger => nil}} # defaults to log everything to the logger
10
+
11
+ initializer "validation_rage.configure_subscribers" do |app|
12
+ # TODO move this into a testable setup method and refactor! -
13
+
14
+ if app.config.validation_rage # if validation rage is enabled
15
+ app.config.validation_rage.notifier.each do |name, options|
16
+ options[:logger] ||= Rails.logger
17
+ ValidationRage.const_get("#{name}Notifier").new(options).subscribe! # initialize the notifier and subscribe to the active suppot notification messages
18
+ end
19
+
20
+ # attach validation rage integration extensions to controllers (with after filter) or models (with after_validation filters)
21
+ controllers = {}
22
+ app.config.validation_rage.attach_to.each do |klass_or_action|
23
+ # if its a string we collect the controller and actions to later add a after_filter
24
+ if klass_or_action.is_a?(String)
25
+ controller, action = klass_or_action.split("#")
26
+ controllers[controller] ||= []
27
+ controllers[controller] << action
28
+ else
29
+ klass.send(:include, ValidationRage::ModelExtension)
30
+ end
31
+ end
32
+ # now addingt the after filter to controllers
33
+ controllers.each do |controller, actions|
34
+ controller = "Application" if controller == "*"
35
+ controller_class = "#{controller}Controller".classify.constantize rescue next # just ignore errors for now
36
+ controller_class.send(:include, ValidationRage::ControllerExtension)
37
+ options = {:only => actions} unless actions.any? {|a| a == "*"}
38
+ controller_class.send(:after_filter, :notify_validation_rage, options)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,17 @@
1
+ require "socket"
2
+ module ValidationRage
3
+ class UdpNotifier < BaseNotifier
4
+
5
+ attr_accessor :socket, :host, :port
6
+ def initialize(args)
7
+ self.socket = UDPSocket.new
8
+ self.host = args[:host]
9
+ self.port = args[:port]
10
+ end
11
+
12
+ def call(event_name, payload)
13
+ self.socket.send(payload.to_json, 0, self.host, self.port)
14
+ end
15
+ end
16
+
17
+ end
@@ -0,0 +1,19 @@
1
+ require "socket"
2
+ module ValidationRage
3
+ class ValidationRageNotifier < BaseNotifier
4
+
5
+ attr_accessor :socket, :host, :port, :api_key
6
+ def initialize(args)
7
+ self.socket = UDPSocket.new
8
+ self.host = "localhost"
9
+ self.port = 33333
10
+ self.api_key = args[:api_key]
11
+ end
12
+
13
+ def call(event_name, payload)
14
+ data = {:api_key => self.api_key, :payload => payload}
15
+ self.socket.send(data.to_json, 0, self.host, self.port)
16
+ end
17
+ end
18
+
19
+ end
@@ -0,0 +1,3 @@
1
+ module ValidationRage
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,13 @@
1
+ require "validation_rage/version"
2
+ require "active_support/notifications"
3
+ require 'validation_rage/rails' if defined?(Rails)
4
+ require "json"
5
+ module ValidationRage
6
+ autoload :BaseNotifier, "validation_rage/base_notifier"
7
+ autoload :LogNotifier, "validation_rage/log_notifier"
8
+ autoload :UdpNotifier, "validation_rage/udp_notifier"
9
+ autoload :ValidationRageNotifier, "validation_rage/validation_rage_notifier"
10
+ autoload :FnordMetricNotifier, "validation_rage/fnord_metric_notifier"
11
+ autoload :ModelExtension, "validation_rage/model_extension"
12
+ autoload :ControllerExtension, "validation_rage/controller_extension"
13
+ end
@@ -0,0 +1,21 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require 'bundler/setup'
3
+ Bundler.require
4
+ require 'minitest/autorun'
5
+ require 'minitest/mock'
6
+ require "mocha"
7
+
8
+ require 'validation_rage'
9
+
10
+
11
+ class MockModel
12
+ def self.after_validation(method)
13
+ @@after_validation_method = method
14
+ end
15
+ def self.after_validation_method
16
+ @@after_validation_method
17
+ end
18
+ def self.name
19
+ "MockClass"
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ require "test_helper"
2
+
3
+ class ValidationRage::BaseNotifierTest < MiniTest::Unit::TestCase
4
+
5
+ def test_call_raises_a_not_implemented_error
6
+ assert_raises(ValidationRage::BaseNotifier::NotImplementedError) { ValidationRage::BaseNotifier.new.call("event", {}) }
7
+ end
8
+
9
+ def test_subscribe_to_validation_rage_events
10
+ notifier = ValidationRage::BaseNotifier.new
11
+ ActiveSupport::Notifications.expects(:subscribe).with(/validation_rage:.*/, notifier)
12
+ notifier.subscribe!
13
+ end
14
+
15
+ def test_data_present
16
+ notifier = ValidationRage::BaseNotifier.new
17
+ assert notifier.data_present?("klass" => {:error => :message})
18
+ assert !notifier.data_present?("klass" => {})
19
+ assert !notifier.data_present?({})
20
+ end
21
+ end
@@ -0,0 +1,60 @@
1
+ require "test_helper"
2
+
3
+ # TODO: guess this test could be written nicer
4
+ class MockController
5
+ def request
6
+ OpenStruct.new(:filtered_parameters => ["filtered", "params"], :path => "/mock/path", :uuid => "uuid", :referrer => "referrer")
7
+ end
8
+ def params
9
+ {:controller => "Mock", :action => "create"}
10
+ end
11
+ def initialize(records={})
12
+ records.each do |name,record|
13
+ instance_variable_set("@#{name}", record)
14
+ end
15
+ end
16
+ end
17
+ class ValidationRage::ControllerExtensionTest < MiniTest::Unit::TestCase
18
+
19
+ def setup
20
+ @klass = Class.new(MockController)
21
+ @klass.send(:include, ValidationRage::ControllerExtension)
22
+ end
23
+
24
+ def test_errors_present
25
+ assert !@klass.new(:var => "not responding to errors").validation_rage_errors_present?(:"@var")
26
+ assert !@klass.new(:var => mock(:errors => [])).validation_rage_errors_present?(:"@var")
27
+ assert !@klass.new(:_var => "internal only vars should be ignored").validation_rage_errors_present?(:"@_var")
28
+ assert @klass.new(:var => mock(:errors => ["not empty"])).validation_rage_errors_present?(:"@var")
29
+ end
30
+
31
+ def test_validation_rage_context
32
+ assert_equal({
33
+ :controller => "Mock",
34
+ :action => "create",
35
+ :request_path => "/mock/path",
36
+ :params => ["filtered","params"],
37
+ :request_uuid => "uuid",
38
+ :referrer => "referrer"
39
+ }, @klass.new.validation_rage_context)
40
+ end
41
+
42
+ def test_notify_validation_rage
43
+ record = MockModel.new
44
+ record.expects(:errors).at_least_once.returns(mock(:"empty?"=>false, :to_hash=>{:errors => :hash}))
45
+ controller_instance = @klass.new(:record => record, :foo => :bar)
46
+ ActiveSupport::Notifications.expects(:publish).with("validation_rage:errors", {
47
+ "MockClass" => {:errors => :hash},
48
+ :context => {
49
+ :controller => "Mock",
50
+ :action => "create",
51
+ :request_path => "/mock/path",
52
+ :params => ["filtered","params"],
53
+ :request_uuid => "uuid",
54
+ :referrer => "referrer"
55
+ }
56
+ })
57
+ controller_instance.notify_validation_rage
58
+ end
59
+
60
+ end
@@ -0,0 +1,23 @@
1
+ require "test_helper"
2
+
3
+ class ValidationRage::LogNotifierTest < MiniTest::Unit::TestCase
4
+
5
+
6
+ def test_defaul_log_level
7
+ assert_equal :warn, ValidationRage::LogNotifier.new({}).log_level
8
+ end
9
+ def test_log_validation_errors_to_logger
10
+ payload = {"Class" => {:name => ["missing"]}}
11
+ mocked_logger = mock()
12
+ mocked_logger.expects(:info).with("event_name #{payload.inspect}")
13
+ log_notifier = ValidationRage::LogNotifier.new(:logger => mocked_logger, :log_level => :info)
14
+ log_notifier.call("event_name", payload)
15
+ end
16
+
17
+ def test_ignore_empty_errors
18
+ mocked_logger = mock()
19
+ mocked_logger.expects(:warn).never
20
+ notifier = ValidationRage::LogNotifier.new(:logger => mocked_logger)
21
+ notifier.call("event_name", "Class" => {})
22
+ end
23
+ end
@@ -0,0 +1,29 @@
1
+ require "test_helper"
2
+
3
+ # TODO: guess this test could be written nicer
4
+ class ValidationRage::ModelExtensionTest < MiniTest::Unit::TestCase
5
+
6
+ def setup
7
+ @klass = Class.new(MockModel)
8
+ @klass.send(:include, ValidationRage::ModelExtension)
9
+ end
10
+
11
+ def test_register_after_validation_callback
12
+ assert_equal :notify_validation_rage, @klass.after_validation_method
13
+ assert @klass.new.respond_to?(:notify_validation_rage)
14
+ end
15
+
16
+ def test_publish_active_support_notification
17
+ instance = @klass.new
18
+ instance.expects(:errors).returns(mock(:to_hash=>{:errors => :hash}))
19
+ ActiveSupport::Notifications.expects(:publish).with("validation_rage:errors", {"MockClass" => {:errors => :hash}, :context => {}})
20
+ instance.notify_validation_rage
21
+ end
22
+
23
+ def test_passing_a_context_hash_to_notification_call
24
+ instance = @klass.new
25
+ instance.expects(:errors).returns(mock(:to_hash=>{:errors => :hash}))
26
+ ActiveSupport::Notifications.expects(:publish).with("validation_rage:errors", {"MockClass" => {:errors => :hash}, :context => {:controller => "Users", :action => "edit"}})
27
+ instance.notify_validation_rage({:controller => "Users", :action => "edit"})
28
+ end
29
+ end
@@ -0,0 +1,21 @@
1
+ require "test_helper"
2
+ require "socket"
3
+ class ValidationRage::UdpNotifierTest < MiniTest::Unit::TestCase
4
+
5
+ def test_connect_to_socket
6
+ UDPSocket.expects(:new).returns("socket")
7
+ notifier = ValidationRage::UdpNotifier.new(:host => "localhost", :port => 33333)
8
+ assert_equal "socket", notifier.socket
9
+ assert_equal "localhost", notifier.host
10
+ assert_equal 33333, notifier.port
11
+ end
12
+
13
+ def test_send_json_encoded_payload_through_socket
14
+ notifier = ValidationRage::UdpNotifier.new(:host => "host", :port => "port")
15
+ payload = {"Class" => {:error => :message}}
16
+ socket = mock()
17
+ socket.expects(:send).with(payload.to_json,0, "host", "port")
18
+ notifier.expects(:socket).returns(socket)
19
+ notifier.call("event_name", payload)
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/validation_rage/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Michael Bumann"]
6
+ gem.email = ["michael@railslove.com"]
7
+ gem.description = %q{log active model validation errors to get better insights in the usability of your forms}
8
+ gem.summary = %q{log active model validation errors to get better insights in the usability of your forms}
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "validation_rage"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = ValidationRage::VERSION
17
+ gem.add_development_dependency 'rake'
18
+ gem.add_development_dependency 'mocha'
19
+ gem.add_dependency 'activesupport'
20
+ gem.add_dependency 'json'
21
+ end
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: validation_rage
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Michael Bumann
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: &2156416240 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *2156416240
25
+ - !ruby/object:Gem::Dependency
26
+ name: mocha
27
+ requirement: &2156459260 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *2156459260
36
+ - !ruby/object:Gem::Dependency
37
+ name: activesupport
38
+ requirement: &2156470080 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *2156470080
47
+ - !ruby/object:Gem::Dependency
48
+ name: json
49
+ requirement: &2156465740 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *2156465740
58
+ description: log active model validation errors to get better insights in the usability
59
+ of your forms
60
+ email:
61
+ - michael@railslove.com
62
+ executables: []
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - .gitignore
67
+ - .travis.yml
68
+ - Gemfile
69
+ - LICENSE
70
+ - README.md
71
+ - Rakefile
72
+ - lib/validation_rage.rb
73
+ - lib/validation_rage/base_notifier.rb
74
+ - lib/validation_rage/controller_extension.rb
75
+ - lib/validation_rage/fnord_metric_notifier.rb
76
+ - lib/validation_rage/log_notifier.rb
77
+ - lib/validation_rage/model_extension.rb
78
+ - lib/validation_rage/rails.rb
79
+ - lib/validation_rage/udp_notifier.rb
80
+ - lib/validation_rage/validation_rage_notifier.rb
81
+ - lib/validation_rage/version.rb
82
+ - test/test_helper.rb
83
+ - test/validation_rage/base_notifier_test.rb
84
+ - test/validation_rage/controller_extension_test.rb
85
+ - test/validation_rage/log_notifier_test.rb
86
+ - test/validation_rage/model_extension_test.rb
87
+ - test/validation_rage/udp_notifier_test.rb
88
+ - validation_rage.gemspec
89
+ homepage: ''
90
+ licenses: []
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ! '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ! '>='
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 1.8.10
110
+ signing_key:
111
+ specification_version: 3
112
+ summary: log active model validation errors to get better insights in the usability
113
+ of your forms
114
+ test_files:
115
+ - test/test_helper.rb
116
+ - test/validation_rage/base_notifier_test.rb
117
+ - test/validation_rage/controller_extension_test.rb
118
+ - test/validation_rage/log_notifier_test.rb
119
+ - test/validation_rage/model_extension_test.rb
120
+ - test/validation_rage/udp_notifier_test.rb
121
+ has_rdoc: