cat_forms 0.0.2 → 0.0.3
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.
- checksums.yaml +4 -4
- data/lib/cat_forms/gzip_cookie.rb +35 -0
- data/lib/cat_forms/validation_scopes.rb +66 -0
- data/lib/cat_forms/version.rb +3 -0
- data/lib/cat_forms.rb +254 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e41399d5ac4893087435e2e6aa5955d4d3ee284a
|
4
|
+
data.tar.gz: b0a5e377693ef76d875bc2b87ce9495fb9e480de
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e721107373b10dfd0557b58b905fd3a6f58420e70ce11c3eace372f325643463b41796092bdc4fa8a86c1c96903dabed26431d0a4c5a5929d109ca0ca512cf7
|
7
|
+
data.tar.gz: cd56fde1729f815dbc07bd1e7057f71a81852076f15d188a5e777dbad12a67b2fb1c82e2b43e6c97f91f8bc738975c789b98a821aef49950b3fc9b9616210f8f
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'zlib'
|
3
|
+
require 'active_support/hash_with_indifferent_access'
|
4
|
+
|
5
|
+
# Saves form as cookie as json+gzip
|
6
|
+
module CatForms::GzipCookie
|
7
|
+
def self.load options={}
|
8
|
+
request = options[:request]
|
9
|
+
cookie_name = options[:cookie_name].to_s
|
10
|
+
result = ActiveSupport::HashWithIndifferentAccess.new
|
11
|
+
return result if request.blank?
|
12
|
+
cookie = request.cookies[cookie_name]
|
13
|
+
return result if cookie.nil? or cookie.empty?
|
14
|
+
begin
|
15
|
+
result.merge!(ActiveSupport::JSON.decode(Zlib::Inflate.inflate(cookie)).stringify_keys)
|
16
|
+
return result
|
17
|
+
rescue Zlib::DataError
|
18
|
+
return result
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.save options = {}
|
23
|
+
attributes = options[:attributes]
|
24
|
+
response = options[:response]
|
25
|
+
cookie_name = options[:cookie_name]
|
26
|
+
|
27
|
+
cookie_json = ActiveSupport::JSON.encode(attributes)
|
28
|
+
cookie_json = Zlib::Deflate.deflate(cookie_json, Zlib::BEST_COMPRESSION)
|
29
|
+
cookie_hash = { value: cookie_json,
|
30
|
+
httponly: true,
|
31
|
+
}
|
32
|
+
response.set_cookie(cookie_name, cookie_hash)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module ValidationScopes
|
4
|
+
def self.included(base) # :nodoc:
|
5
|
+
base.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def validation_scope(scope)
|
10
|
+
@all_scopes ||= []
|
11
|
+
@all_scopes << scope
|
12
|
+
|
13
|
+
def self.all_scopes
|
14
|
+
@all_scopes
|
15
|
+
end
|
16
|
+
|
17
|
+
base_class = self
|
18
|
+
|
19
|
+
proxy_class = Class.new(DelegateClass(base_class)) do
|
20
|
+
include ActiveModel::Validations
|
21
|
+
|
22
|
+
def initialize(record)
|
23
|
+
@base_record = record
|
24
|
+
super(record)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Hacks to support dynamic_model helpers
|
28
|
+
def to_model
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
# Rails 3 default implementation of model_name blows up for anonymous classes
|
33
|
+
class << self; self; end.class_eval do
|
34
|
+
define_method(:model_name) do
|
35
|
+
base_class.model_name
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
yield proxy_class
|
41
|
+
|
42
|
+
define_method(scope) do
|
43
|
+
send("validation_scope_proxy_for_#{scope}").errors
|
44
|
+
end
|
45
|
+
|
46
|
+
define_method("no_#{scope}?") do
|
47
|
+
send("validation_scope_proxy_for_#{scope}").valid?
|
48
|
+
end
|
49
|
+
|
50
|
+
define_method("has_#{scope}?") do
|
51
|
+
send("validation_scope_proxy_for_#{scope}").invalid?
|
52
|
+
end
|
53
|
+
|
54
|
+
define_method("init_validation_scope_for_#{scope}") do
|
55
|
+
unless instance_variable_defined?("@#{scope}")
|
56
|
+
instance_variable_set("@#{scope}", proxy_class.new(self))
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
define_method("validation_scope_proxy_for_#{scope}") do
|
61
|
+
send "init_validation_scope_for_#{scope}"
|
62
|
+
instance_variable_get("@#{scope}")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/cat_forms.rb
ADDED
@@ -0,0 +1,254 @@
|
|
1
|
+
# Warning: This code is atrocious.
|
2
|
+
#
|
3
|
+
# ActiveModel is sorta confusing in Rails 3.2. I think
|
4
|
+
# it's improved in 4.0?
|
5
|
+
#
|
6
|
+
# TODO we should be checking the type of things when saving
|
7
|
+
|
8
|
+
module CatForms
|
9
|
+
require 'active_model'
|
10
|
+
require 'virtus'
|
11
|
+
require 'active_support/core_ext/class/attribute'
|
12
|
+
require 'cat_forms/validation_scopes'
|
13
|
+
autoload :GzipCookie, 'cat_forms/gzip_cookie'
|
14
|
+
|
15
|
+
# TODO not sure thy this is necessary. tests run, but
|
16
|
+
# when this is included by rails, i get already defined
|
17
|
+
# errors and super class mismatch errors.
|
18
|
+
if !defined?(CatForms::Boolean)
|
19
|
+
class Boolean < Virtus::Attribute::Boolean
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module Form
|
24
|
+
|
25
|
+
def self.included base
|
26
|
+
base.send :include, ActiveModel::Validations
|
27
|
+
base.send :extend, ActiveModel::Callbacks
|
28
|
+
base.send :include, Virtus.model
|
29
|
+
base.send :extend, ClassMethods
|
30
|
+
base.send :include, InstanceMethods
|
31
|
+
base.send :include, ValidationScopes
|
32
|
+
base.instance_eval do
|
33
|
+
define_model_callbacks :initialize, :save
|
34
|
+
class_attribute :_form_name
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module ClassMethods
|
39
|
+
# Overrides how Virtus handles attributes of the wrong type.
|
40
|
+
# Ideally virtus would coerce everything to the right type,
|
41
|
+
# use the default, or return nil.
|
42
|
+
# There's a related discussion here: https://github.com/solnic/virtus/issues/99
|
43
|
+
def attribute(name, type, opts = {})
|
44
|
+
super(name, type, opts)
|
45
|
+
define_method(name) do
|
46
|
+
data = super()
|
47
|
+
default_value = -> { opts[:default].respond_to?(:call) ? opts[:default].call : opts[:default] }
|
48
|
+
|
49
|
+
# If they want a BigDecimal or Int, but data is a string, strip
|
50
|
+
# the stuff of bad data and try to convert manually.
|
51
|
+
if type == BigDecimal or type == Integer
|
52
|
+
if data.class == String
|
53
|
+
data.gsub!(/[^\d\.]/, '')
|
54
|
+
if data.present?
|
55
|
+
if type == BigDecimal
|
56
|
+
data = BigDecimal.new(data)
|
57
|
+
else
|
58
|
+
data = Integer(data)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
#if type.class != Array and !data.is_a?(type) and !data.nil? and (type != CatForms::Boolean and !data.is_a?(Boolean))
|
65
|
+
#puts "cat forms expected a #{type} for #{ name }, received #{ data.inspect }"
|
66
|
+
#puts caller
|
67
|
+
#end
|
68
|
+
|
69
|
+
if type == CatForms::Boolean and (data == true or data == false)
|
70
|
+
return data
|
71
|
+
end
|
72
|
+
|
73
|
+
if type.class == Array
|
74
|
+
# Not validating more right now.
|
75
|
+
return data
|
76
|
+
end
|
77
|
+
|
78
|
+
if data.is_a?(type)
|
79
|
+
# Override for default string
|
80
|
+
if type == String and opts.has_key?(:default) and data == ""
|
81
|
+
return default_value.call
|
82
|
+
else
|
83
|
+
return data
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
return default_value.call
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def form_name name
|
92
|
+
name = name.to_s
|
93
|
+
|
94
|
+
def name.i18n_key
|
95
|
+
self
|
96
|
+
end
|
97
|
+
|
98
|
+
def name.human
|
99
|
+
self
|
100
|
+
end
|
101
|
+
|
102
|
+
def name.singular
|
103
|
+
self
|
104
|
+
end
|
105
|
+
|
106
|
+
def name.plural
|
107
|
+
self
|
108
|
+
end
|
109
|
+
|
110
|
+
def name.param_key
|
111
|
+
self
|
112
|
+
end
|
113
|
+
|
114
|
+
self.class.instance_eval do
|
115
|
+
define_method :model_name do
|
116
|
+
name
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
self._form_name = name
|
121
|
+
end
|
122
|
+
|
123
|
+
def model_name
|
124
|
+
self._form_name || super
|
125
|
+
end
|
126
|
+
|
127
|
+
def form_attribute name, klass, options={}
|
128
|
+
# Setup the default values for each attribute.
|
129
|
+
if !options.has_key?(:default)
|
130
|
+
options[:default] =
|
131
|
+
if klass.kind_of?(Array)
|
132
|
+
[]
|
133
|
+
elsif klass == BigDecimal or klass == Boolean or klass == CatForms::Boolean
|
134
|
+
nil
|
135
|
+
elsif klass.respond_to?(:new)
|
136
|
+
proc { klass.new }
|
137
|
+
else
|
138
|
+
nil
|
139
|
+
end
|
140
|
+
end
|
141
|
+
attribute name, klass, options
|
142
|
+
if options[:validations]
|
143
|
+
validates(name, options[:validations])
|
144
|
+
end
|
145
|
+
|
146
|
+
# Define an association_attributes= method
|
147
|
+
# Rails's fields_for looks for this.
|
148
|
+
if klass.kind_of?(Array)
|
149
|
+
define_method "#{name}_attributes=" do |hash|
|
150
|
+
hash.each do |_index, opts|
|
151
|
+
# If the array is nil, set it to an empty array.
|
152
|
+
self.send("#{name}=", []) if self.send(name).nil?
|
153
|
+
self.send(name) << klass.first.new(opts)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def custom_attribute attribute_name
|
160
|
+
class_eval do
|
161
|
+
attr_reader attribute_name
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def validates_associated(*associations)
|
166
|
+
validates_each(associations) do |record, _associate_name, value|
|
167
|
+
(value.respond_to?(:each) ? value : [value]).each do |rec|
|
168
|
+
if rec && !rec.valid?
|
169
|
+
rec.errors.each do |key, val|
|
170
|
+
record.errors.add(key, val)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
module InstanceMethods
|
179
|
+
# Need to look into all the below methods, not sure if they are
|
180
|
+
# correct.
|
181
|
+
def persisted?
|
182
|
+
false
|
183
|
+
end
|
184
|
+
|
185
|
+
def to_model
|
186
|
+
self
|
187
|
+
end
|
188
|
+
|
189
|
+
def to_partial_path
|
190
|
+
"some_path" # TODO figure out what's needed here for Rails 3.2
|
191
|
+
end
|
192
|
+
|
193
|
+
def to_param
|
194
|
+
nil
|
195
|
+
end
|
196
|
+
|
197
|
+
def to_key
|
198
|
+
nil
|
199
|
+
end
|
200
|
+
|
201
|
+
def save
|
202
|
+
run_callbacks :save do
|
203
|
+
if !valid?
|
204
|
+
return false
|
205
|
+
end
|
206
|
+
end
|
207
|
+
self
|
208
|
+
end
|
209
|
+
|
210
|
+
def initialize options = {}
|
211
|
+
run_callbacks(:initialize) do
|
212
|
+
# can sometimes be a string
|
213
|
+
if options.class != Hash
|
214
|
+
options = {}
|
215
|
+
end
|
216
|
+
options[:form] ||= {}
|
217
|
+
|
218
|
+
# TODO fix
|
219
|
+
# This allows setting of custom things
|
220
|
+
options.each do |key, value|
|
221
|
+
instance_variable_set "@#{key}", value
|
222
|
+
end
|
223
|
+
|
224
|
+
CatForms::GzipCookie.load(storage_options).each do |key, value|
|
225
|
+
method = "#{key}="
|
226
|
+
if respond_to?(method)
|
227
|
+
self.send method, value
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
options[:form].each do |name, value|
|
232
|
+
value.strip! if value.respond_to?(:strip)
|
233
|
+
self.send "#{name}=", value
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
237
|
+
super
|
238
|
+
end
|
239
|
+
|
240
|
+
def save_to_storage!
|
241
|
+
options = storage_options.merge(attributes: attributes)
|
242
|
+
CatForms::GzipCookie.save(options)
|
243
|
+
end
|
244
|
+
|
245
|
+
def storage_options
|
246
|
+
{
|
247
|
+
cookie_name: @cookie_name,
|
248
|
+
request: @request,
|
249
|
+
response: @response
|
250
|
+
}
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cat_forms
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joe Van Dyk
|
@@ -44,7 +44,11 @@ email:
|
|
44
44
|
executables: []
|
45
45
|
extensions: []
|
46
46
|
extra_rdoc_files: []
|
47
|
-
files:
|
47
|
+
files:
|
48
|
+
- lib/cat_forms.rb
|
49
|
+
- lib/cat_forms/gzip_cookie.rb
|
50
|
+
- lib/cat_forms/validation_scopes.rb
|
51
|
+
- lib/cat_forms/version.rb
|
48
52
|
homepage: https://github.com/joevandyk/cat_forms
|
49
53
|
licenses: []
|
50
54
|
metadata: {}
|