validation_rage 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +80 -0
- data/Rakefile +9 -0
- data/lib/validation_rage/base_notifier.rb +18 -0
- data/lib/validation_rage/controller_extension.rb +28 -0
- data/lib/validation_rage/fnord_metric_notifier.rb +34 -0
- data/lib/validation_rage/log_notifier.rb +14 -0
- data/lib/validation_rage/model_extension.rb +12 -0
- data/lib/validation_rage/rails.rb +43 -0
- data/lib/validation_rage/udp_notifier.rb +17 -0
- data/lib/validation_rage/validation_rage_notifier.rb +19 -0
- data/lib/validation_rage/version.rb +3 -0
- data/lib/validation_rage.rb +13 -0
- data/test/test_helper.rb +21 -0
- data/test/validation_rage/base_notifier_test.rb +21 -0
- data/test/validation_rage/controller_extension_test.rb +60 -0
- data/test/validation_rage/log_notifier_test.rb +23 -0
- data/test/validation_rage/model_extension_test.rb +29 -0
- data/test/validation_rage/udp_notifier_test.rb +21 -0
- data/validation_rage.gemspec +21 -0
- metadata +121 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
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,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,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
|
data/test/test_helper.rb
ADDED
@@ -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:
|