poncho 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|