monkey_forms 0.0.15
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 +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
|