poncho 0.0.2
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 +18 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +302 -0
- data/Rakefile +8 -0
- data/examples/app.rb +80 -0
- data/lib/poncho.rb +27 -0
- data/lib/poncho/error.rb +75 -0
- data/lib/poncho/errors.rb +142 -0
- data/lib/poncho/filters.rb +52 -0
- data/lib/poncho/json_method.rb +47 -0
- data/lib/poncho/method.rb +271 -0
- data/lib/poncho/param.rb +29 -0
- data/lib/poncho/params.rb +92 -0
- data/lib/poncho/params/array.rb +15 -0
- data/lib/poncho/params/boolean.rb +20 -0
- data/lib/poncho/params/boolean_string.rb +19 -0
- data/lib/poncho/params/integer.rb +17 -0
- data/lib/poncho/params/object.rb +15 -0
- data/lib/poncho/params/resource.rb +27 -0
- data/lib/poncho/params/string.rb +15 -0
- data/lib/poncho/params/validations.rb +24 -0
- data/lib/poncho/request.rb +71 -0
- data/lib/poncho/resource.rb +80 -0
- data/lib/poncho/response.rb +28 -0
- data/lib/poncho/returns.rb +25 -0
- data/lib/poncho/validations.rb +198 -0
- data/lib/poncho/validations/exclusions.rb +77 -0
- data/lib/poncho/validations/format.rb +105 -0
- data/lib/poncho/validations/inclusions.rb +77 -0
- data/lib/poncho/validations/length.rb +123 -0
- data/lib/poncho/validations/presence.rb +49 -0
- data/lib/poncho/validator.rb +172 -0
- data/lib/poncho/version.rb +3 -0
- data/poncho.gemspec +19 -0
- data/test/poncho/test_method.rb +105 -0
- data/test/poncho/test_resource.rb +71 -0
- metadata +84 -0
@@ -0,0 +1,123 @@
|
|
1
|
+
module Poncho
|
2
|
+
module Validations
|
3
|
+
class LengthValidator < EachValidator
|
4
|
+
MESSAGES = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long }.freeze
|
5
|
+
CHECKS = { :is => :==, :minimum => :>=, :maximum => :<= }.freeze
|
6
|
+
|
7
|
+
RESERVED_OPTIONS = [:minimum, :maximum, :within, :is, :tokenizer, :too_short, :too_long]
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
if range = (options.delete(:in) || options.delete(:within))
|
11
|
+
raise ArgumentError, ":in and :within must be a Range" unless range.is_a?(Range)
|
12
|
+
options[:minimum], options[:maximum] = range.begin, range.end
|
13
|
+
options[:maximum] -= 1 if range.exclude_end?
|
14
|
+
end
|
15
|
+
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
def check_validity!
|
20
|
+
keys = CHECKS.keys & options.keys
|
21
|
+
|
22
|
+
if keys.empty?
|
23
|
+
raise ArgumentError, 'Range unspecified. Specify the :in, :within, :maximum, :minimum, or :is option.'
|
24
|
+
end
|
25
|
+
|
26
|
+
keys.each do |key|
|
27
|
+
value = options[key]
|
28
|
+
|
29
|
+
unless value.is_a?(Integer) && value >= 0
|
30
|
+
raise ArgumentError, ":#{key} must be a nonnegative Integer"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def validate_each(record, attribute, value)
|
36
|
+
value = tokenize(value)
|
37
|
+
value_length = value.respond_to?(:length) ? value.length : value.to_s.length
|
38
|
+
|
39
|
+
CHECKS.each do |key, validity_check|
|
40
|
+
next unless check_value = options[key]
|
41
|
+
next if value_length.send(validity_check, check_value)
|
42
|
+
|
43
|
+
errors_options = options.dup
|
44
|
+
errors_options[:count] = check_value
|
45
|
+
|
46
|
+
default_message = options[MESSAGES[key]]
|
47
|
+
errors_options[:message] ||= default_message if default_message
|
48
|
+
|
49
|
+
record.errors.add(attribute, MESSAGES[key], errors_options)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def tokenize(value)
|
56
|
+
if value.kind_of?(String) && options[:tokenizer]
|
57
|
+
return options[:tokenizer].call(value)
|
58
|
+
end
|
59
|
+
|
60
|
+
value
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
module HelperMethods
|
65
|
+
# Validates that the specified attribute matches the length restrictions supplied.
|
66
|
+
# Only one option can be used at a time:
|
67
|
+
#
|
68
|
+
# class Person < ActiveRecord::Base
|
69
|
+
# validates_length_of :first_name, :maximum => 30
|
70
|
+
# validates_length_of :last_name, :maximum => 30, :message => "less than 30 if you don't mind"
|
71
|
+
# validates_length_of :fax, :in => 7..32, :allow_nil => true
|
72
|
+
# validates_length_of :phone, :in => 7..32, :allow_blank => true
|
73
|
+
# validates_length_of :user_name, :within => 6..20, :too_long => "pick a shorter name", :too_short => "pick a longer name"
|
74
|
+
# validates_length_of :zip_code, :minimum => 5, :too_short => "please enter at least 5 characters"
|
75
|
+
# validates_length_of :smurf_leader, :is => 4, :message => "papa is spelled with 4 characters... don't play me."
|
76
|
+
# validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least 100 words.",
|
77
|
+
# :tokenizer => lambda { |str| str.scan(/\w+/) }
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
# Configuration options:
|
81
|
+
# * <tt>:minimum</tt> - The minimum size of the attribute.
|
82
|
+
# * <tt>:maximum</tt> - The maximum size of the attribute.
|
83
|
+
# * <tt>:is</tt> - The exact size of the attribute.
|
84
|
+
# * <tt>:within</tt> - A range specifying the minimum and maximum size of the
|
85
|
+
# attribute.
|
86
|
+
# * <tt>:in</tt> - A synonym(or alias) for <tt>:within</tt>.
|
87
|
+
# * <tt>:allow_nil</tt> - Attribute may be +nil+; skip validation.
|
88
|
+
# * <tt>:allow_blank</tt> - Attribute may be blank; skip validation.
|
89
|
+
# * <tt>:too_long</tt> - The error message if the attribute goes over the
|
90
|
+
# maximum (default is: "is too long (maximum is %{count} characters)").
|
91
|
+
# * <tt>:too_short</tt> - The error message if the attribute goes under the
|
92
|
+
# minimum (default is: "is too short (min is %{count} characters)").
|
93
|
+
# * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt> method
|
94
|
+
# and the attribute is the wrong size (default is: "is the wrong length
|
95
|
+
# should be %{count} characters)").
|
96
|
+
# * <tt>:message</tt> - The error message to use for a <tt>:minimum</tt>,
|
97
|
+
# <tt>:maximum</tt>, or <tt>:is</tt> violation. An alias of the appropriate
|
98
|
+
# <tt>too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message.
|
99
|
+
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
|
100
|
+
# validation contexts by default (+nil+), other options are <tt>:create</tt>
|
101
|
+
# and <tt>:update</tt>.
|
102
|
+
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if
|
103
|
+
# the validation should occur (e.g. <tt>:if => :allow_validation</tt>, or
|
104
|
+
# <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
|
105
|
+
# or string should return or evaluate to a true or false value.
|
106
|
+
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
|
107
|
+
# if the validation should not occur (e.g. <tt>:unless => :skip_validation</tt>,
|
108
|
+
# or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
109
|
+
# method, proc or string should return or evaluate to a true or false value.
|
110
|
+
# * <tt>:tokenizer</tt> - Specifies how to split up the attribute string.
|
111
|
+
# (e.g. <tt>:tokenizer => lambda {|str| str.scan(/\w+/)}</tt> to count words
|
112
|
+
# as in above example). Defaults to <tt>lambda{ |value| value.split(//) }</tt> which counts individual characters.
|
113
|
+
# * <tt>:strict</tt> - Specifies whether validation should be strict.
|
114
|
+
# See <tt>Poncho::Validation#validates!</tt> for more information.
|
115
|
+
def validates_length_of(*attr_names)
|
116
|
+
options = attr_names.last.is_a?(::Hash) ? attr_names.pop : {}
|
117
|
+
validates_with LengthValidator, options.merge(:attributes => attr_names)
|
118
|
+
end
|
119
|
+
|
120
|
+
alias_method :validates_size_of, :validates_length_of
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Poncho
|
2
|
+
module Validations
|
3
|
+
class PresenceValidator < EachValidator
|
4
|
+
def validate_each(record, attribute, value)
|
5
|
+
if !value || value == ""
|
6
|
+
record.errors.add(attribute, :presence, options.merge(:value => value))
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module HelperMethods
|
12
|
+
# Validates that the specified attributes are not blank (as defined by
|
13
|
+
# Object#blank?). Happens by default on save. Example:
|
14
|
+
#
|
15
|
+
# class Person < ActiveRecord::Base
|
16
|
+
# validates_presence_of :first_name
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# The first_name attribute must be in the object and it cannot be blank.
|
20
|
+
#
|
21
|
+
# If you want to validate the presence of a boolean field (where the real values
|
22
|
+
# are true and false), you will want to use <tt>validates_inclusion_of :field_name,
|
23
|
+
# :in => [true, false]</tt>.
|
24
|
+
#
|
25
|
+
# This is due to the way Object#blank? handles boolean values:
|
26
|
+
# <tt>false.blank? # => true</tt>.
|
27
|
+
#
|
28
|
+
# Configuration options:
|
29
|
+
# * <tt>:message</tt> - A custom error message (default is: "can't be blank").
|
30
|
+
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
|
31
|
+
# validation contexts by default (+nil+), other options are <tt>:create</tt>
|
32
|
+
# and <tt>:update</tt>.
|
33
|
+
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if
|
34
|
+
# the validation should occur (e.g. <tt>:if => :allow_validation</tt>, or
|
35
|
+
# <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
|
36
|
+
# or string should return or evaluate to a true or false value.
|
37
|
+
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
|
38
|
+
# if the validation should not occur (e.g. <tt>:unless => :skip_validation</tt>,
|
39
|
+
# or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The method,
|
40
|
+
# proc or string should return or evaluate to a true or false value.
|
41
|
+
# * <tt>:strict</tt> - Specifies whether validation should be strict.
|
42
|
+
# See <tt>Poncho::Validation#validates!</tt> for more information.
|
43
|
+
def validates_presence_of(*attr_names)
|
44
|
+
options = attr_names.last.is_a?(::Hash) ? attr_names.pop : {}
|
45
|
+
validates_with PresenceValidator, options.merge(:attributes => attr_names)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
module Poncho
|
2
|
+
#
|
3
|
+
# class Person
|
4
|
+
# include Poncho::Validations
|
5
|
+
# validates_with MyValidator
|
6
|
+
# end
|
7
|
+
#
|
8
|
+
# class MyValidator < Poncho::Validator
|
9
|
+
# def validate(record)
|
10
|
+
# if some_complex_logic
|
11
|
+
# record.errors[:base] = "This record is invalid"
|
12
|
+
# end
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# private
|
16
|
+
# def some_complex_logic
|
17
|
+
# # ...
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# Any class that inherits from Poncho::Validator must implement a method
|
22
|
+
# called <tt>validate</tt> which accepts a <tt>record</tt>.
|
23
|
+
#
|
24
|
+
# class Person
|
25
|
+
# include Poncho::Validations
|
26
|
+
# validates_with MyValidator
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# class MyValidator < Poncho::Validator
|
30
|
+
# def validate(record)
|
31
|
+
# record # => The person instance being validated
|
32
|
+
# options # => Any non-standard options passed to validates_with
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# To cause a validation error, you must add to the <tt>record</tt>'s errors directly
|
37
|
+
# from within the validators message
|
38
|
+
#
|
39
|
+
# class MyValidator < Poncho::Validator
|
40
|
+
# def validate(record)
|
41
|
+
# record.errors.add :base, "This is some custom error message"
|
42
|
+
# record.errors.add :first_name, "This is some complex validation"
|
43
|
+
# # etc...
|
44
|
+
# end
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# To add behavior to the initialize method, use the following signature:
|
48
|
+
#
|
49
|
+
# class MyValidator < Poncho::Validator
|
50
|
+
# def initialize(options)
|
51
|
+
# super
|
52
|
+
# @my_custom_field = options[:field_name] || :first_name
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# The easiest way to add custom validators for validating individual attributes
|
57
|
+
# is with the convenient <tt>Poncho::EachValidator</tt>. For example:
|
58
|
+
#
|
59
|
+
# class TitleValidator < Poncho::EachValidator
|
60
|
+
# def validate_each(record, attribute, value)
|
61
|
+
# record.errors.add attribute, 'must be Mr. Mrs. or Dr.' unless value.in?(['Mr.', 'Mrs.', 'Dr.'])
|
62
|
+
# end
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# This can now be used in combination with the +validates+ method
|
66
|
+
# (see <tt>Poncho::Validations::ClassMethods.validates</tt> for more on this)
|
67
|
+
#
|
68
|
+
# class Person
|
69
|
+
# include Poncho::Validations
|
70
|
+
# attr_accessor :title
|
71
|
+
#
|
72
|
+
# validates :title, :presence => true
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# Validator may also define a +setup+ instance method which will get called
|
76
|
+
# with the class that using that validator as its argument. This can be
|
77
|
+
# useful when there are prerequisites such as an +attr_accessor+ being present
|
78
|
+
# for example:
|
79
|
+
#
|
80
|
+
# class MyValidator < Poncho::Validator
|
81
|
+
# def setup(klass)
|
82
|
+
# klass.send :attr_accessor, :custom_attribute
|
83
|
+
# end
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# This setup method is only called when used with validation macros or the
|
87
|
+
# class level <tt>validates_with</tt> method.
|
88
|
+
#
|
89
|
+
class Validator
|
90
|
+
def self.kind
|
91
|
+
@kind ||= begin
|
92
|
+
full_name = name.split('::').last
|
93
|
+
full_name = full_name.gsub(/([a-z\d])([A-Z])/,'\1_\2').downcase
|
94
|
+
full_name.sub(/_validator$/, '').to_sym
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
attr_reader :options
|
99
|
+
|
100
|
+
# Accepts options that will be made available through the +options+ reader.
|
101
|
+
def initialize(options)
|
102
|
+
@options = options.freeze
|
103
|
+
end
|
104
|
+
|
105
|
+
# Override this method in subclasses with validation logic, adding errors
|
106
|
+
# to the records +errors+ array where necessary.
|
107
|
+
def validate(record)
|
108
|
+
raise NotImplementedError, "Subclasses must implement a validate(record) method."
|
109
|
+
end
|
110
|
+
|
111
|
+
def kind
|
112
|
+
self.class.kind
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# +EachValidator+ is a validator which iterates through the attributes given
|
117
|
+
# in the options hash invoking the <tt>validate_each</tt> method passing in the
|
118
|
+
# record, attribute and value.
|
119
|
+
#
|
120
|
+
# All Poncho validations are built on top of this validator.
|
121
|
+
class EachValidator < Validator
|
122
|
+
attr_reader :attributes
|
123
|
+
|
124
|
+
# Returns a new validator instance. All options will be available via the
|
125
|
+
# +options+ reader, however the <tt>:attributes</tt> option will be removed
|
126
|
+
# and instead be made available through the +attributes+ reader.
|
127
|
+
def initialize(options)
|
128
|
+
@attributes = Array(options.delete(:attributes))
|
129
|
+
raise ':attributes cannot be blank' if @attributes.empty?
|
130
|
+
super
|
131
|
+
check_validity!
|
132
|
+
end
|
133
|
+
|
134
|
+
# Performs validation on the supplied record. By default this will call
|
135
|
+
# +validates_each+ to determine validity therefore subclasses should
|
136
|
+
# override +validates_each+ with validation logic.
|
137
|
+
def validate(record)
|
138
|
+
attributes.each do |attribute|
|
139
|
+
value = record.read_attribute_for_validation(attribute)
|
140
|
+
next if (value.nil? && options[:allow_nil]) || (value == "" && options[:allow_blank])
|
141
|
+
validate_each(record, attribute, value)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Override this method in subclasses with the validation logic, adding
|
146
|
+
# errors to the records +errors+ array where necessary.
|
147
|
+
def validate_each(record, attribute, value)
|
148
|
+
raise NotImplementedError, 'Subclasses must implement a validate_each(record, attribute, value) method'
|
149
|
+
end
|
150
|
+
|
151
|
+
# Hook method that gets called by the initializer allowing verification
|
152
|
+
# that the arguments supplied are valid. You could for example raise an
|
153
|
+
# +ArgumentError+ when invalid options are supplied.
|
154
|
+
def check_validity!
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# +BlockValidator+ is a special +EachValidator+ which receives a block on initialization
|
159
|
+
# and call this block for each attribute being validated. +validates_each+ uses this validator.
|
160
|
+
class BlockValidator < EachValidator
|
161
|
+
def initialize(options, &block)
|
162
|
+
@block = block
|
163
|
+
super
|
164
|
+
end
|
165
|
+
|
166
|
+
private
|
167
|
+
|
168
|
+
def validate_each(record, attribute, value)
|
169
|
+
@block.call(record, attribute, value)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
data/poncho.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'poncho/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "poncho"
|
8
|
+
gem.version = Poncho::VERSION
|
9
|
+
gem.authors = ["Alex MacCaw"]
|
10
|
+
gem.email = ["alex@stripe.com"]
|
11
|
+
gem.description = %q{Poncho is an API to build REST APIs with a convenient DSL.}
|
12
|
+
gem.summary = %q{Poncho is an API to build APIs}
|
13
|
+
gem.homepage = "https://github.com/stripe/poncho"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'rack/mock'
|
3
|
+
require 'poncho'
|
4
|
+
|
5
|
+
class TestMethod < MiniTest::Unit::TestCase
|
6
|
+
def env(params = {})
|
7
|
+
Rack::MockRequest.env_for('http://api.com/charges', :params => params)
|
8
|
+
end
|
9
|
+
|
10
|
+
def setup
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_that_integer_params_are_validated
|
14
|
+
method = Class.new(Poncho::Method) do
|
15
|
+
param :amount, :type => :integer
|
16
|
+
end
|
17
|
+
|
18
|
+
status, headers, body = method.call(env(:amount => nil))
|
19
|
+
assert_equal 406, status
|
20
|
+
|
21
|
+
status, headers, body = method.call(env(:amount => '1'))
|
22
|
+
assert_equal 200, status
|
23
|
+
|
24
|
+
status, headers, body = method.call(env(:amount => 'blah'))
|
25
|
+
assert_equal 406, status
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_that_string_params_are_validated
|
29
|
+
method = Class.new(Poncho::Method) do
|
30
|
+
param :amount, :type => :string
|
31
|
+
end
|
32
|
+
|
33
|
+
status, headers, body = method.call(env(:amount => nil))
|
34
|
+
assert_equal 406, status
|
35
|
+
|
36
|
+
status, headers, body = method.call(env(:amount => 'blah'))
|
37
|
+
assert_equal 200, status
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_presence_validation
|
41
|
+
method = Class.new(Poncho::Method) do
|
42
|
+
param :amount, :required => true
|
43
|
+
end
|
44
|
+
|
45
|
+
status, headers, body = method.call(env())
|
46
|
+
assert_equal 406, status
|
47
|
+
|
48
|
+
status, headers, body = method.call(env(:amount => 'test'))
|
49
|
+
assert_equal 200, status
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_custom_param_conversion
|
53
|
+
custom_param = Class.new(Poncho::Param) do
|
54
|
+
def convert(value)
|
55
|
+
return true if value == 'custom'
|
56
|
+
return false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
method = Class.new(Poncho::Method) do
|
61
|
+
param :currency, :type => custom_param
|
62
|
+
|
63
|
+
def invoke
|
64
|
+
halt param(:currency) == true ? 200 : 406
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
status, headers, body = method.call(env(:currency => 'notcustom'))
|
69
|
+
assert_equal 406, status
|
70
|
+
|
71
|
+
status, headers, body = method.call(env(:currency => 'custom'))
|
72
|
+
assert_equal 200, status
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_custom_param_validation
|
76
|
+
custom_param = Class.new(Poncho::Param) do
|
77
|
+
def validate_each(record, name, value)
|
78
|
+
unless ['USD', 'GBP'].include?(value)
|
79
|
+
record.errors.add(name, :invalid_currency)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
method = Class.new(Poncho::Method) do
|
85
|
+
param :currency, :type => custom_param
|
86
|
+
end
|
87
|
+
|
88
|
+
status, headers, body = method.call(env(:currency => 'RSU'))
|
89
|
+
assert_equal 406, status
|
90
|
+
|
91
|
+
status, headers, body = method.call(env(:currency => 'USD'))
|
92
|
+
assert_equal 200, status
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_json_method_returns_json
|
96
|
+
method = Class.new(Poncho::JSONMethod) do
|
97
|
+
def invoke
|
98
|
+
{:some => 'stuff'}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
status, headers, body = method.call(env())
|
103
|
+
assert_equal({:some => 'stuff'}.to_json, body.body.first)
|
104
|
+
end
|
105
|
+
end
|