monkey_forms 0.0.15
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/BUGS +1 -0
- data/Gemfile +9 -0
- data/NOTES +4 -0
- data/README.md +154 -0
- data/Rakefile +13 -0
- data/lib/monkey_forms/serializers/active_record_session.rb +12 -0
- data/lib/monkey_forms/serializers/gzip_cookie.rb +34 -0
- data/lib/monkey_forms/serializers/message_pack_json.rb +32 -0
- data/lib/monkey_forms/serializers.rb +7 -0
- data/lib/monkey_forms/version.rb +3 -0
- data/lib/monkey_forms.rb +146 -0
- data/monkey_forms.gemspec +31 -0
- data/test/forms/basic_form.rb +8 -0
- data/test/forms/order_form.rb +54 -0
- data/test/forms/order_form_child.rb +4 -0
- data/test/sinatra/config.ru +5 -0
- data/test/sinatra/sample_sinatra.rb +50 -0
- data/test/sinatra/views/form.haml +25 -0
- data/test/test_monkey_forms.rb +182 -0
- metadata +179 -0
data/.gitignore
ADDED
data/BUGS
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
- Forms that inherit from other forms don't seem have their form_name set correctly (according to Rails, anyways).
|
data/Gemfile
ADDED
data/NOTES
ADDED
data/README.md
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
NOTE! If you use the cookie serialization feature, you
|
2
|
+
need to use this version of deep_merge for now.
|
3
|
+
https://github.com/joevandyk/deep_merge
|
4
|
+
|
5
|
+
|
6
|
+
MonkeyForms::Form is an ActiveModel-compliant interface
|
7
|
+
between your controllers and models (or whatever you are
|
8
|
+
saving the data to).
|
9
|
+
|
10
|
+
MonkeyForms supports multi-page wizards and validation groups.
|
11
|
+
|
12
|
+
class OrderForm
|
13
|
+
include MonkeyForms::Form
|
14
|
+
form_name 'cart'
|
15
|
+
form_attributes :name, :address, :city, :state, :zip
|
16
|
+
|
17
|
+
validates :name, :presence => true
|
18
|
+
|
19
|
+
# Save the data however you want.
|
20
|
+
def save
|
21
|
+
address = Address.new(:street => address, :city => city, :state => state, :zip => zip)
|
22
|
+
OrderService.place_order(:the_name => name, :address => address)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
In your controller:
|
28
|
+
def new
|
29
|
+
@cart = OrderForm.new
|
30
|
+
end
|
31
|
+
def create
|
32
|
+
@cart = OrderForm.new(:form => params[:cart])
|
33
|
+
if @cart.valid?
|
34
|
+
@cart.save
|
35
|
+
redirect_to "/thanks"
|
36
|
+
else
|
37
|
+
render :action => 'new'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
In your view:
|
43
|
+
= form_for @cart do |f|
|
44
|
+
= f.text_field :name
|
45
|
+
= f.text_field :address
|
46
|
+
= f.text_field :city
|
47
|
+
= # etc
|
48
|
+
= f.submit
|
49
|
+
|
50
|
+
|
51
|
+
A more complex multi-page order process.
|
52
|
+
State is remembered in a cookie; the form params are merged into the cookie's state on each request.
|
53
|
+
|
54
|
+
class OrderForm
|
55
|
+
include MonkeyForms::Form
|
56
|
+
|
57
|
+
# Declares a few attributes on the form.
|
58
|
+
form_attributes :name, :email, :city, :state, :line_items
|
59
|
+
custom_attributes :user_id
|
60
|
+
form_name :cart
|
61
|
+
|
62
|
+
# This form serializes the submit into a gzip'd cookie with a name
|
63
|
+
# of 'order_cookie'.
|
64
|
+
set_form_storage(
|
65
|
+
MonkeyForms::Serializers::GzipCookie.new(
|
66
|
+
:name => 'order_cookie',
|
67
|
+
:domain => 'test.domain.com',
|
68
|
+
:secure => true,
|
69
|
+
:httponly => true))
|
70
|
+
|
71
|
+
after_initialize :set_default_state
|
72
|
+
|
73
|
+
# We must submit an email address for the form to validate.
|
74
|
+
validates :email, :presence => true
|
75
|
+
|
76
|
+
validation_group :cart do
|
77
|
+
# Scope some of the validation checks
|
78
|
+
validates :name, :presence => true
|
79
|
+
end
|
80
|
+
|
81
|
+
validation_group :address do
|
82
|
+
validates :city, :presence => true
|
83
|
+
validates :state, :presence => true
|
84
|
+
end
|
85
|
+
|
86
|
+
# This is a method that uses some form attributes.
|
87
|
+
def person
|
88
|
+
"#{ name } <#{ email }>"
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def set_default_state
|
94
|
+
if state.blank?
|
95
|
+
self.state = "WA"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
class Controller
|
102
|
+
before_filter :load_cart
|
103
|
+
# Name / Email
|
104
|
+
def page1
|
105
|
+
if request.post?
|
106
|
+
if group.valid?(:name)
|
107
|
+
redirect_to "/page2"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Address
|
113
|
+
def page2
|
114
|
+
if request.post?
|
115
|
+
if group.valid?(:address)
|
116
|
+
# whatever..
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
def load_cart
|
124
|
+
@cart = OrderForm::Form.new(:form => params[:cart]
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
The validation_group code is modified from
|
130
|
+
https://github.com/adzap/grouped_validations
|
131
|
+
|
132
|
+
|
133
|
+
This is pretty similar to the Presenter Pattern as described
|
134
|
+
by Jay Fields, by the way. http://blog.jayfields.com/2007/03/rails-presenter-pattern.html
|
135
|
+
|
136
|
+
|
137
|
+
|
138
|
+
There is a sample sinatra application in test/sinatra. Run with:
|
139
|
+
cd test/sinatra
|
140
|
+
rackup config.ru
|
141
|
+
|
142
|
+
|
143
|
+
|
144
|
+
??? WHY ???
|
145
|
+
|
146
|
+
Moving the form logic to a separate class has a ton of advantages:
|
147
|
+
|
148
|
+
* Keeps the controller really simple.
|
149
|
+
* Makes it easier to test. You can write tests directly against the form handling class.
|
150
|
+
* Classes should do one thing.
|
151
|
+
* You can have complex validations.
|
152
|
+
* Your ActiveRecord models can probably become simpler.
|
153
|
+
* Since the form handling logic is encapsulated into one class, you can use inheritance, modules, etc.
|
154
|
+
* You want to move away from ActiveRecord? It's no problem -- just change how the form values are saved in the #save method.
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'bundler/setup'
|
5
|
+
|
6
|
+
Bundler::GemHelper.install_tasks
|
7
|
+
|
8
|
+
Rake::TestTask.new(:test) do |test|
|
9
|
+
test.libs << 'lib' << 'test'
|
10
|
+
test.pattern = 'test/**/test_*.rb'
|
11
|
+
end
|
12
|
+
|
13
|
+
task :default => :test
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'zlib'
|
3
|
+
|
4
|
+
# Saves form as cookie as json+gzip
|
5
|
+
class MonkeyForms::Serializers::GzipCookie
|
6
|
+
def initialize options={}
|
7
|
+
@cookie_name = options[:name]
|
8
|
+
@domain = options[:domain]
|
9
|
+
@secure = options[:secure]
|
10
|
+
@httponly = options[:httponly]
|
11
|
+
end
|
12
|
+
|
13
|
+
def load options={}
|
14
|
+
request = options[:request]
|
15
|
+
result = ActiveSupport::HashWithIndifferentAccess.new
|
16
|
+
return result if request.blank?
|
17
|
+
cookie = request.cookies[@cookie_name]
|
18
|
+
return result if cookie.nil? or cookie.empty?
|
19
|
+
result.merge!(ActiveSupport::JSON.decode(Zlib::Inflate.inflate(cookie)).stringify_keys)
|
20
|
+
end
|
21
|
+
|
22
|
+
def save options = {}
|
23
|
+
attributes = options[:attributes]
|
24
|
+
response = options[:response]
|
25
|
+
|
26
|
+
cookie_json = ActiveSupport::JSON.encode(attributes)
|
27
|
+
cookie_json = Zlib::Deflate.deflate(cookie_json, Zlib::BEST_COMPRESSION)
|
28
|
+
cookie_hash = { :value => cookie_json,
|
29
|
+
:httponly => @httponly,
|
30
|
+
:secure => @secure,
|
31
|
+
:domain => @domain }
|
32
|
+
response.set_cookie(@cookie_name, cookie_hash)
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'msgpack'
|
2
|
+
|
3
|
+
# Saves form as cookie as json+gzip
|
4
|
+
class MonkeyForms::Serializers::MessagePackJson
|
5
|
+
def initialize options={}
|
6
|
+
@cookie_name = options[:name]
|
7
|
+
@domain = options[:domain]
|
8
|
+
@secure = options[:secure]
|
9
|
+
@httponly = options[:httponly]
|
10
|
+
end
|
11
|
+
|
12
|
+
def load options={}
|
13
|
+
request = options[:request]
|
14
|
+
result = ActiveSupport::HashWithIndifferentAccess.new
|
15
|
+
return result if request.blank?
|
16
|
+
cookie = request.cookies[@cookie_name]
|
17
|
+
return result if cookie.blank?
|
18
|
+
result.merge!(ActiveSupport::JSON.decode(MessagePack.unpack(cookie)).stringify_keys)
|
19
|
+
end
|
20
|
+
|
21
|
+
def save options = {}
|
22
|
+
attributes = options[:attributes]
|
23
|
+
response = options[:response]
|
24
|
+
|
25
|
+
cookie_json = ActiveSupport::JSON.encode(attributes).to_msgpack
|
26
|
+
cookie_hash = { :value => cookie_json,
|
27
|
+
:httponly => @httponly,
|
28
|
+
:secure => @secure,
|
29
|
+
:domain => @domain }
|
30
|
+
response.set_cookie(@cookie_name, cookie_hash)
|
31
|
+
end
|
32
|
+
end
|
data/lib/monkey_forms.rb
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
module MonkeyForms
|
2
|
+
require 'monkey_forms/serializers'
|
3
|
+
require 'active_model'
|
4
|
+
require 'active_support/hash_with_indifferent_access'
|
5
|
+
require 'active_support/core_ext/object/try'
|
6
|
+
require 'grouped_validations'
|
7
|
+
require 'deep_merge'
|
8
|
+
|
9
|
+
module Form
|
10
|
+
|
11
|
+
def self.included base
|
12
|
+
base.send :include, ActiveModel::Validations
|
13
|
+
base.send :extend, ActiveModel::Callbacks
|
14
|
+
base.send :include, InstanceMethods
|
15
|
+
base.send :extend, ClassMethods
|
16
|
+
base.instance_eval do
|
17
|
+
define_model_callbacks :initialize
|
18
|
+
end
|
19
|
+
base.send :include, ActiveModel::Validations
|
20
|
+
end
|
21
|
+
|
22
|
+
module InstanceMethods
|
23
|
+
attr_reader :attributes
|
24
|
+
|
25
|
+
def persisted?
|
26
|
+
false
|
27
|
+
end
|
28
|
+
|
29
|
+
# TODO not sure what's best here
|
30
|
+
def html_error_messages
|
31
|
+
errors.full_messages.join("<br />")
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_model
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_param
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_key
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
|
46
|
+
def initialize options = {}
|
47
|
+
_run_initialize_callbacks do
|
48
|
+
form_params = options.delete(:form) || {}
|
49
|
+
@options = options
|
50
|
+
|
51
|
+
@options.each do |key, value|
|
52
|
+
instance_variable_set "@#{key}", value
|
53
|
+
end
|
54
|
+
|
55
|
+
# Load the saved form from storage
|
56
|
+
@attributes =
|
57
|
+
if self.class.form_storage
|
58
|
+
self.class.form_storage.load(@options)
|
59
|
+
else
|
60
|
+
ActiveSupport::HashWithIndifferentAccess.new
|
61
|
+
end
|
62
|
+
|
63
|
+
# Merge in this form's params
|
64
|
+
DeepMerge.deep_merge!(form_params, @attributes)
|
65
|
+
|
66
|
+
self.class.attributes.each do |a|
|
67
|
+
@attributes[a] ||= ""
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def save_to_storage!
|
73
|
+
@options[:attributes] = @attributes
|
74
|
+
self.class.form_storage.save(@options)
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
module ClassMethods
|
80
|
+
attr_reader :form_storage
|
81
|
+
|
82
|
+
def attributes
|
83
|
+
@attributes ||= {}
|
84
|
+
end
|
85
|
+
|
86
|
+
# Compatibility with ActiveModel::Naming
|
87
|
+
def model_name
|
88
|
+
if !defined?(@_model_name)
|
89
|
+
# EWWWW
|
90
|
+
@_model_name = (@_form_name.try(:to_s) ||
|
91
|
+
superclass.instance_variable_get(:@_form_name).try(:to_s) ||
|
92
|
+
name.underscore).try(:underscore)
|
93
|
+
%w( singular human i18n_key partial_path plural ).each do |method|
|
94
|
+
@_model_name.class_eval do
|
95
|
+
define_method method do
|
96
|
+
self
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
@_model_name
|
102
|
+
end
|
103
|
+
|
104
|
+
def form_name name
|
105
|
+
@_form_name = name
|
106
|
+
end
|
107
|
+
|
108
|
+
def set_form_storage storage_object
|
109
|
+
@form_storage = storage_object
|
110
|
+
end
|
111
|
+
|
112
|
+
def set_form_attribute_human_names options
|
113
|
+
@_form_attribute_names ||= {}
|
114
|
+
@_form_attribute_names.merge!(options)
|
115
|
+
end
|
116
|
+
|
117
|
+
def human_attribute_name name, *options
|
118
|
+
@_form_attribute_names ||= {}
|
119
|
+
@_form_attribute_names[name] || super
|
120
|
+
end
|
121
|
+
|
122
|
+
def form_attributes *attrs
|
123
|
+
@attributes ||= []
|
124
|
+
attrs.each do |attr|
|
125
|
+
@attributes << attr
|
126
|
+
|
127
|
+
# Defines public method
|
128
|
+
define_method attr do
|
129
|
+
@attributes[attr.to_s]
|
130
|
+
end
|
131
|
+
define_method "#{attr}=" do |value|
|
132
|
+
@attributes[attr.to_s] = value
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def custom_attributes *attrs
|
138
|
+
attrs.each do |attr|
|
139
|
+
instance_eval do
|
140
|
+
attr_reader attr
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "monkey_forms/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "monkey_forms"
|
7
|
+
s.version = MonkeyForms::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Joe Van Dyk"]
|
10
|
+
s.email = ["joe@tanga.com"]
|
11
|
+
s.homepage = "https://github.com/joevandyk/monkey_forms"
|
12
|
+
s.summary = %q{Helps make complex forms}
|
13
|
+
s.description = %q{Helps make complex forms}
|
14
|
+
s.rubyforge_project = "monkey_forms"
|
15
|
+
|
16
|
+
s.add_dependency('activemodel')
|
17
|
+
s.add_dependency('deep_merge')
|
18
|
+
|
19
|
+
s.add_development_dependency 'minitest'
|
20
|
+
s.add_development_dependency 'rake'
|
21
|
+
s.add_development_dependency 'sinatra'
|
22
|
+
s.add_development_dependency 'rack-test'
|
23
|
+
s.add_development_dependency 'shotgun'
|
24
|
+
s.add_development_dependency 'haml'
|
25
|
+
s.add_development_dependency('msgpack')
|
26
|
+
|
27
|
+
s.files = `git ls-files`.split("\n")
|
28
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
29
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
30
|
+
s.require_paths = ["lib"]
|
31
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'monkey_forms'
|
2
|
+
|
3
|
+
class OrderForm
|
4
|
+
include MonkeyForms::Form
|
5
|
+
attr_reader :an_attribute
|
6
|
+
|
7
|
+
# Declares a few attributes on the form.
|
8
|
+
form_attributes :name, :email, :city, :state, :line_items
|
9
|
+
custom_attributes :user_id
|
10
|
+
form_name :cart
|
11
|
+
|
12
|
+
# This form serializes the submit into a gzip'd cookie with a name
|
13
|
+
# of 'order_cookie'.
|
14
|
+
set_form_storage(
|
15
|
+
MonkeyForms::Serializers::GzipCookie.new(
|
16
|
+
:name => 'order_cookie',
|
17
|
+
:domain => 'test.domain.com',
|
18
|
+
:secure => true,
|
19
|
+
:httponly => true))
|
20
|
+
|
21
|
+
after_initialize :set_attribute, :set_default_state
|
22
|
+
|
23
|
+
# We must submit an email address for the form to validate.
|
24
|
+
validates :email, :presence => true
|
25
|
+
|
26
|
+
set_form_attribute_human_names :name => "Your Name"
|
27
|
+
|
28
|
+
validation_group :cart do
|
29
|
+
# Scope some of the validation checks
|
30
|
+
validates :name, :presence => true
|
31
|
+
end
|
32
|
+
|
33
|
+
validation_group :address do
|
34
|
+
validates :city, :presence => true
|
35
|
+
validates :state, :presence => true
|
36
|
+
end
|
37
|
+
|
38
|
+
# This is a method that uses some form attributes.
|
39
|
+
def person
|
40
|
+
"#{ name } <#{ email }>"
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def set_attribute
|
46
|
+
@an_attribute = true
|
47
|
+
end
|
48
|
+
|
49
|
+
def set_default_state
|
50
|
+
if state.blank?
|
51
|
+
self.state = "WA"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'forms/order_form'
|
3
|
+
|
4
|
+
class SampleSinatra < Sinatra::Base
|
5
|
+
#set :show_exceptions, false
|
6
|
+
|
7
|
+
before do
|
8
|
+
load_form
|
9
|
+
end
|
10
|
+
|
11
|
+
get "/" do
|
12
|
+
@page = 1
|
13
|
+
haml :form
|
14
|
+
end
|
15
|
+
|
16
|
+
get "/2" do
|
17
|
+
@page = 2
|
18
|
+
haml :form
|
19
|
+
end
|
20
|
+
|
21
|
+
get "/3" do
|
22
|
+
@page = 3
|
23
|
+
haml :form
|
24
|
+
end
|
25
|
+
|
26
|
+
post "/haml_form" do
|
27
|
+
# We could probably do something with the form object here, maybe
|
28
|
+
# Tells the form to serialize itself.
|
29
|
+
@form.save_to_storage!
|
30
|
+
redirect "/"
|
31
|
+
end
|
32
|
+
|
33
|
+
# Only used in unit tests
|
34
|
+
post "/form" do
|
35
|
+
# Tells the form to serialize itself.
|
36
|
+
@form.save_to_storage!
|
37
|
+
|
38
|
+
# Renders something (used in tests)
|
39
|
+
@form.person
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def load_form
|
45
|
+
@form = OrderForm.new(:form => request.params["form"],
|
46
|
+
:request => request,
|
47
|
+
:response => response)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
%h1 Welcome to Monkey Forms Page #{ @page }
|
2
|
+
|
3
|
+
%h3
|
4
|
+
%a{:href => "/"} Page 1
|
5
|
+
|
|
6
|
+
%a{:href => "/2"} Page 2
|
7
|
+
|
|
8
|
+
%a{:href => "/3"} Page 3
|
9
|
+
|
10
|
+
%h3 Form Values
|
11
|
+
= @form.attributes.inspect
|
12
|
+
|
13
|
+
%p
|
14
|
+
As you click from page to page, note that your form
|
15
|
+
values are saved. They are saved in a cookie!
|
16
|
+
|
17
|
+
%form{:action => '/haml_form', :method => 'post'}
|
18
|
+
- @form.class.attributes.each do |name|
|
19
|
+
%input{:type => 'text', :id => "form_#{name}", :name => "form[#{name}]", :value => @form.send(name)}
|
20
|
+
%label{:for => "form_#{name}"}= name
|
21
|
+
|
22
|
+
%br
|
23
|
+
|
24
|
+
%input{:type => 'submit'}
|
25
|
+
|
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'minitest/pride'
|
3
|
+
require 'rack/test'
|
4
|
+
|
5
|
+
require 'sinatra/sample_sinatra'
|
6
|
+
require 'forms/order_form'
|
7
|
+
require 'forms/order_form_child'
|
8
|
+
require 'forms/basic_form'
|
9
|
+
|
10
|
+
# I want to ensure this library works fine with all of ActiveSupport loaded
|
11
|
+
require 'active_support/all'
|
12
|
+
|
13
|
+
class TestMonkeyForms < MiniTest::Unit::TestCase
|
14
|
+
include Rack::Test::Methods
|
15
|
+
|
16
|
+
def app
|
17
|
+
SampleSinatra.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_form_post_with_cookie
|
21
|
+
post "https://test.domain.com/form", :form => { :name => "Joe" }
|
22
|
+
assert_equal "Joe <>", last_response.body
|
23
|
+
post "https://test.domain.com/form", :form => { :email => "joe@tanga.com" }
|
24
|
+
assert_equal "Joe <joe@tanga.com>", last_response.body
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_attribute_name
|
28
|
+
o = OrderForm.new
|
29
|
+
o.valid?
|
30
|
+
assert o.errors.full_messages.include?("Your Name can't be blank")
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_form_works_without_human_names_set
|
34
|
+
o = BasicForm.new
|
35
|
+
o.valid?
|
36
|
+
assert o.errors.full_messages.include?("Name can't be blank")
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_form_cookie
|
40
|
+
submit_form :name => "joe"
|
41
|
+
# TODO figure out how to get test the cookie stuff properly
|
42
|
+
assert last_response.headers["Set-Cookie"].include?("HttpOnly")
|
43
|
+
assert last_response.headers["Set-Cookie"].include?("secure")
|
44
|
+
assert last_response.headers["Set-Cookie"].include?("test.domain.com")
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_setters_from_inside_form
|
48
|
+
a = submit_form :name => 'joe'
|
49
|
+
assert_equal 'WA', a[:state]
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_child_form
|
53
|
+
o = OrderFormChild.new :form => { :child => 'new', :name => 'joe' }
|
54
|
+
assert_equal 'new', o.child
|
55
|
+
assert_equal 'joe', o.name
|
56
|
+
|
57
|
+
assert_equal 'cart', o.class.model_name
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_basic
|
61
|
+
o = OrderForm.new :form => { :name => "Joe", :email => "joe@tanga.com" }
|
62
|
+
assert_equal "Joe", o.name
|
63
|
+
assert_equal "joe@tanga.com", o.email
|
64
|
+
assert_equal "", o.city
|
65
|
+
assert_equal 5, o.attributes.size
|
66
|
+
assert_equal "Joe", o.attributes[:name]
|
67
|
+
assert_equal "Joe", o.attributes["name"]
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_validation
|
71
|
+
o = OrderForm.new :form => { :name => "Joe" }
|
72
|
+
o.valid?
|
73
|
+
assert_equal ["can't be blank"], o.errors[:email]
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_validation_before_valid_called
|
77
|
+
o = OrderForm.new
|
78
|
+
assert_equal [], o.errors[:name]
|
79
|
+
assert_equal [], o.errors[:email]
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_html_errors
|
83
|
+
o = OrderForm.new
|
84
|
+
o.valid?
|
85
|
+
assert o.html_error_messages.include?("City can't be blank")
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_validation_scope_after_valid_called
|
89
|
+
o = OrderForm.new
|
90
|
+
refute o.valid?
|
91
|
+
assert ["can't be blank"], o.errors[:name]
|
92
|
+
assert ["can't be blank"], o.errors[:email]
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_group_validation
|
96
|
+
o = OrderForm.new
|
97
|
+
refute o.group_valid?(:cart)
|
98
|
+
assert_equal ["can't be blank"], o.errors[:name]
|
99
|
+
assert_equal [], o.errors[:email]
|
100
|
+
|
101
|
+
o.valid?
|
102
|
+
assert_equal ["can't be blank"], o.errors[:email]
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_after_initialize
|
106
|
+
o = OrderForm.new
|
107
|
+
assert o.an_attribute
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_set_custom_attributes
|
111
|
+
o = OrderForm.new :user_id => 1
|
112
|
+
assert_equal 1, o.user_id
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_can_access_attributes
|
116
|
+
o = OrderForm.new :form => { :email => "joe@tanga.com" , :name => "Joe" }
|
117
|
+
assert_equal "Joe <joe@tanga.com>", o.person
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_array
|
121
|
+
submit_form(:line_items => [ {:id => "previous"}])
|
122
|
+
attributes = submit_form(:line_items => [ {:id => "new"}])
|
123
|
+
|
124
|
+
assert_equal 2, attributes[:line_items].size
|
125
|
+
assert_equal "previous", attributes[:line_items].first[:id]
|
126
|
+
assert_equal "new", attributes[:line_items].last[:id]
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_array_update
|
130
|
+
submit_form(:line_items => [ {:id => "first", :quantity => 1}])
|
131
|
+
attributes = submit_form(:line_items => [ {:id => "first", :quantity => 3}])
|
132
|
+
assert_equal "3", attributes[:line_items].last[:quantity]
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_submit_blank
|
136
|
+
a = submit_form(:name => "Joe", :city => "Seattle")
|
137
|
+
assert_equal "Joe", a[:name]
|
138
|
+
|
139
|
+
a = submit_form(:name => "", :city => "Seattle")
|
140
|
+
assert_equal "", a[:name]
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
def extract_attributes request
|
145
|
+
serializer = MonkeyForms::Serializers::GzipCookie.new(:name => "order_cookie")
|
146
|
+
serializer.load(:request => request)
|
147
|
+
end
|
148
|
+
|
149
|
+
def submit_form attributes
|
150
|
+
post "https://test.domain.com/form", :form => attributes
|
151
|
+
post "https://test.domain.com/form", :form => {} # not sure why the cookies don't get set in last_request without this
|
152
|
+
extract_attributes last_request
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
class TestMonkeyFormsActiveModelLint < MiniTest::Unit::TestCase
|
157
|
+
include ActiveModel::Lint::Tests
|
158
|
+
|
159
|
+
def setup
|
160
|
+
@model = OrderForm.new
|
161
|
+
end
|
162
|
+
|
163
|
+
def test_form_name
|
164
|
+
assert_equal "cart", @model.class.model_name
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
class TestMonkeyFormsBasic < MiniTest::Unit::TestCase
|
169
|
+
include ActiveModel::Lint::Tests
|
170
|
+
|
171
|
+
def setup
|
172
|
+
@model = BasicForm.new
|
173
|
+
end
|
174
|
+
|
175
|
+
def test_form_name
|
176
|
+
assert_equal "basic_form", @model.class.model_name
|
177
|
+
end
|
178
|
+
|
179
|
+
def test_attributes
|
180
|
+
assert_equal @model.attributes, {"name" => ""}
|
181
|
+
end
|
182
|
+
end
|
metadata
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: monkey_forms
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.15
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Joe Van Dyk
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-07-03 00:00:00.000000000 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activemodel
|
17
|
+
requirement: &2165026280 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *2165026280
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: deep_merge
|
28
|
+
requirement: &2165025680 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *2165025680
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: minitest
|
39
|
+
requirement: &2165025120 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
type: :development
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *2165025120
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: rake
|
50
|
+
requirement: &2165024680 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
type: :development
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: *2165024680
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: sinatra
|
61
|
+
requirement: &2165024140 !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ! '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
type: :development
|
68
|
+
prerelease: false
|
69
|
+
version_requirements: *2165024140
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: rack-test
|
72
|
+
requirement: &2165023560 !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
type: :development
|
79
|
+
prerelease: false
|
80
|
+
version_requirements: *2165023560
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: shotgun
|
83
|
+
requirement: &2165022960 !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ! '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
type: :development
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: *2165022960
|
92
|
+
- !ruby/object:Gem::Dependency
|
93
|
+
name: haml
|
94
|
+
requirement: &2162564820 !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ! '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
type: :development
|
101
|
+
prerelease: false
|
102
|
+
version_requirements: *2162564820
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: msgpack
|
105
|
+
requirement: &2162564280 !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
type: :development
|
112
|
+
prerelease: false
|
113
|
+
version_requirements: *2162564280
|
114
|
+
description: Helps make complex forms
|
115
|
+
email:
|
116
|
+
- joe@tanga.com
|
117
|
+
executables: []
|
118
|
+
extensions: []
|
119
|
+
extra_rdoc_files: []
|
120
|
+
files:
|
121
|
+
- .gitignore
|
122
|
+
- BUGS
|
123
|
+
- Gemfile
|
124
|
+
- NOTES
|
125
|
+
- README.md
|
126
|
+
- Rakefile
|
127
|
+
- lib/monkey_forms.rb
|
128
|
+
- lib/monkey_forms/serializers.rb
|
129
|
+
- lib/monkey_forms/serializers/active_record_session.rb
|
130
|
+
- lib/monkey_forms/serializers/gzip_cookie.rb
|
131
|
+
- lib/monkey_forms/serializers/message_pack_json.rb
|
132
|
+
- lib/monkey_forms/version.rb
|
133
|
+
- monkey_forms.gemspec
|
134
|
+
- test/forms/basic_form.rb
|
135
|
+
- test/forms/order_form.rb
|
136
|
+
- test/forms/order_form_child.rb
|
137
|
+
- test/sinatra/config.ru
|
138
|
+
- test/sinatra/sample_sinatra.rb
|
139
|
+
- test/sinatra/views/form.haml
|
140
|
+
- test/test_monkey_forms.rb
|
141
|
+
has_rdoc: true
|
142
|
+
homepage: https://github.com/joevandyk/monkey_forms
|
143
|
+
licenses: []
|
144
|
+
post_install_message:
|
145
|
+
rdoc_options: []
|
146
|
+
require_paths:
|
147
|
+
- lib
|
148
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
149
|
+
none: false
|
150
|
+
requirements:
|
151
|
+
- - ! '>='
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
154
|
+
segments:
|
155
|
+
- 0
|
156
|
+
hash: -2757128949908824304
|
157
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
158
|
+
none: false
|
159
|
+
requirements:
|
160
|
+
- - ! '>='
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
version: '0'
|
163
|
+
segments:
|
164
|
+
- 0
|
165
|
+
hash: -2757128949908824304
|
166
|
+
requirements: []
|
167
|
+
rubyforge_project: monkey_forms
|
168
|
+
rubygems_version: 1.6.2
|
169
|
+
signing_key:
|
170
|
+
specification_version: 3
|
171
|
+
summary: Helps make complex forms
|
172
|
+
test_files:
|
173
|
+
- test/forms/basic_form.rb
|
174
|
+
- test/forms/order_form.rb
|
175
|
+
- test/forms/order_form_child.rb
|
176
|
+
- test/sinatra/config.ru
|
177
|
+
- test/sinatra/sample_sinatra.rb
|
178
|
+
- test/sinatra/views/form.haml
|
179
|
+
- test/test_monkey_forms.rb
|