wedge 0.1.17 → 0.1.18
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +8 -1
- data/Makefile +2 -0
- data/Rakefile +8 -4
- data/TODO.md +4 -0
- data/lib/roda/plugins/wedge.rb +14 -96
- data/lib/wedge.rb +9 -4
- data/lib/wedge/component.rb +70 -29
- data/lib/wedge/config.rb +36 -6
- data/lib/wedge/middleware.rb +43 -35
- data/lib/wedge/opal.rb +15 -4
- data/lib/wedge/plugins/ability_list.rb +95 -0
- data/lib/wedge/plugins/current_user.rb +48 -0
- data/lib/wedge/plugins/factory.rb +36 -0
- data/lib/wedge/plugins/form.rb +216 -343
- data/lib/wedge/plugins/{validations.rb → form/validations.rb} +64 -37
- data/lib/wedge/plugins/form_backup.rb +442 -0
- data/lib/wedge/plugins/render.rb +110 -0
- data/lib/wedge/plugins/uploader.rb +2 -2
- data/lib/wedge/utilis/duplicable.rb +104 -0
- data/lib/wedge/utilis/hash.rb +48 -0
- data/lib/wedge/version.rb +1 -1
- data/playground/app/app.rb +27 -11
- data/playground/app/components/abilities.rb +11 -0
- data/playground/app/components/current_user.rb +12 -0
- data/playground/app/components/layout.rb +5 -0
- data/playground/app/components/todo_list.rb +24 -0
- data/playground/app/config/boot.rb +3 -0
- data/playground/app/forms/todo_list_add.rb +9 -0
- data/playground/app/models/user.rb +5 -0
- data/playground/public/css/styles.css +139 -0
- data/playground/public/css/todo_list.css +138 -0
- data/playground/public/todo_list.html +23 -0
- data/playground/src/css/styles.scss +2 -1
- data/playground/src/css/todo_list.scss +165 -0
- data/playground/src/todo_list.slim +17 -0
- data/spec/playground/uploader_spec.rb +27 -29
- data/spec/spec_helper.rb +29 -0
- data/spec/stubs/models/user.rb +15 -0
- data/spec/wedge/plugins/current_user_spec.rb +29 -0
- data/spec/wedge/plugins/factory_spec.rb +16 -0
- data/spec/wedge/plugins/form_spec.rb +119 -0
- data/spec/wedge/plugins/uploader_spec.rb +1 -3
- data/spec/wedge_spec.rb +3 -3
- metadata +28 -3
@@ -0,0 +1,110 @@
|
|
1
|
+
class Wedge
|
2
|
+
module Plugins
|
3
|
+
module Render
|
4
|
+
def display_errors options = {}, &block
|
5
|
+
dom = options.delete(:dom) || _options[:dom]
|
6
|
+
d_errors = errors
|
7
|
+
|
8
|
+
if override_errors = options[:override_errors]
|
9
|
+
d_errors = override_errors
|
10
|
+
end
|
11
|
+
|
12
|
+
keys = options.delete(:keys) || (_options[:key] ? [_options[:key]] : [])
|
13
|
+
|
14
|
+
if extra_errors = options.delete(:errors)
|
15
|
+
extra_errors.each do |key, value|
|
16
|
+
d_errors[key] = value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
d_errors.each do |key, error|
|
21
|
+
d_keys = (keys.dup << key)
|
22
|
+
|
23
|
+
error = error.first
|
24
|
+
|
25
|
+
if error.is_a?(Hash)
|
26
|
+
d_options = options.dup
|
27
|
+
d_options[:keys] = d_keys
|
28
|
+
d_options[:override_errors] = d_errors[key].first
|
29
|
+
|
30
|
+
display_errors d_options, &block
|
31
|
+
elsif !block_given? || block.call(d_keys, error) == false
|
32
|
+
name = d_keys.each_with_index.map do |field, i|
|
33
|
+
i != 0 ? "[#{field}]" : field
|
34
|
+
end.join
|
35
|
+
|
36
|
+
if tmpl = options[:tmpl]
|
37
|
+
if client?
|
38
|
+
field_error_dom = DOM.new(`#{tmpl.dom}[0].outerHTML`)
|
39
|
+
else
|
40
|
+
field_error_dom = DOM.new(tmpl.dom.to_html)
|
41
|
+
end
|
42
|
+
else
|
43
|
+
field_error_dom = DOM.new('<span class="field-error"><span>')
|
44
|
+
end
|
45
|
+
|
46
|
+
field_error_dom.html _error_name(key, error)
|
47
|
+
|
48
|
+
field = dom.find("[name='#{name}']")
|
49
|
+
field.before field_error_dom.dom
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
alias_method :render_errors, :display_errors
|
54
|
+
|
55
|
+
def render_values dom = false, key = false, data = false
|
56
|
+
dom = _options[:dom] unless dom
|
57
|
+
key = _options[:key] if !key && _options.key?(:key)
|
58
|
+
|
59
|
+
dom.find('input, select, textarea') do |element|
|
60
|
+
name = element['name']
|
61
|
+
next if name.nil?
|
62
|
+
name = name.gsub(/\A#{key}/, '') if key
|
63
|
+
keys = name.gsub(/\A\[/, '').gsub(/[^a-z0-9_]/, '|').gsub(/\|\|/, '|').gsub(/\|$/, '').split('|')
|
64
|
+
value = false
|
65
|
+
|
66
|
+
keys.each do |k|
|
67
|
+
begin
|
68
|
+
value = value != false ? value.send(k) : send(k)
|
69
|
+
rescue
|
70
|
+
value = ''
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
case element.name
|
75
|
+
when 'select'
|
76
|
+
element.find('option') do |x|
|
77
|
+
x['selected'] = true if x['value'] == value.to_s
|
78
|
+
end
|
79
|
+
when 'input'
|
80
|
+
if %w(radio checkbox).include? element['type']
|
81
|
+
if element['value'] == value.to_s
|
82
|
+
element['checked'] = true
|
83
|
+
else
|
84
|
+
element.delete 'checked'
|
85
|
+
end
|
86
|
+
else
|
87
|
+
value = sprintf('%.2f', value) if value.is_a? BigDecimal
|
88
|
+
element['value'] = value.to_s
|
89
|
+
end
|
90
|
+
when 'textarea'
|
91
|
+
element.val value.to_s
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def _error_name key, error
|
97
|
+
case error.to_s.to_sym
|
98
|
+
when :not_email
|
99
|
+
'Email Isn\'t Valid.'
|
100
|
+
when :not_present
|
101
|
+
'Required.'
|
102
|
+
when :not_equal
|
103
|
+
'Password does not match.'
|
104
|
+
else
|
105
|
+
!error[/\s/] ? error.to_s.gsub(/_/, ' ').titleize : error
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -3,10 +3,10 @@ class Wedge
|
|
3
3
|
class Uploader < Component
|
4
4
|
name :uploader, :uploader_plugin
|
5
5
|
|
6
|
-
on :compile do
|
6
|
+
on :compile do |for_client|
|
7
7
|
settings = Wedge.config.settings[:uploader]
|
8
8
|
store[:settings] = settings.select do |k, v|
|
9
|
-
|
9
|
+
for_client ? %i(aws_access_key_id bucket).include?(k) : true
|
10
10
|
end if settings
|
11
11
|
end
|
12
12
|
|
@@ -0,0 +1,104 @@
|
|
1
|
+
#--
|
2
|
+
# Most objects are cloneable, but not all. For example you can't dup +nil+:
|
3
|
+
#
|
4
|
+
# nil.dup # => TypeError: can't dup NilClass
|
5
|
+
#
|
6
|
+
# Classes may signal their instances are not duplicable removing +dup+/+clone+
|
7
|
+
# or raising exceptions from them. So, to dup an arbitrary object you normally
|
8
|
+
# use an optimistic approach and are ready to catch an exception, say:
|
9
|
+
#
|
10
|
+
# arbitrary_object.dup rescue object
|
11
|
+
#
|
12
|
+
# Rails dups objects in a few critical spots where they are not that arbitrary.
|
13
|
+
# That rescue is very expensive (like 40 times slower than a predicate), and it
|
14
|
+
# is often triggered.
|
15
|
+
#
|
16
|
+
# That's why we hardcode the following cases and check duplicable? instead of
|
17
|
+
# using that rescue idiom.
|
18
|
+
#++
|
19
|
+
class Object
|
20
|
+
# Can you safely dup this object?
|
21
|
+
#
|
22
|
+
# False for +nil+, +false+, +true+, symbol, number objects;
|
23
|
+
# true otherwise.
|
24
|
+
def duplicable?
|
25
|
+
true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# fix: this is a current hack, as of opal 0.8 you can't #dup/#clone procs
|
30
|
+
if RUBY_ENGINE == 'opal'
|
31
|
+
class Proc
|
32
|
+
def duplicable?
|
33
|
+
false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class NilClass
|
39
|
+
# +nil+ is not duplicable:
|
40
|
+
#
|
41
|
+
# nil.duplicable? # => false
|
42
|
+
# nil.dup # => TypeError: can't dup NilClass
|
43
|
+
def duplicable?
|
44
|
+
false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class FalseClass
|
49
|
+
# +false+ is not duplicable:
|
50
|
+
#
|
51
|
+
# false.duplicable? # => false
|
52
|
+
# false.dup # => TypeError: can't dup FalseClass
|
53
|
+
def duplicable?
|
54
|
+
false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class TrueClass
|
59
|
+
# +true+ is not duplicable:
|
60
|
+
#
|
61
|
+
# true.duplicable? # => false
|
62
|
+
# true.dup # => TypeError: can't dup TrueClass
|
63
|
+
def duplicable?
|
64
|
+
false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class Symbol
|
69
|
+
# Symbols are not duplicable:
|
70
|
+
#
|
71
|
+
# :my_symbol.duplicable? # => false
|
72
|
+
# :my_symbol.dup # => TypeError: can't dup Symbol
|
73
|
+
def duplicable?
|
74
|
+
false
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class Numeric
|
79
|
+
# Numbers are not duplicable:
|
80
|
+
#
|
81
|
+
# 3.duplicable? # => false
|
82
|
+
# 3.dup # => TypeError: can't dup Fixnum
|
83
|
+
def duplicable?
|
84
|
+
false
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
defined?(::BigDecimal) or require 'bigdecimal' unless RUBY_ENGINE == 'opal'
|
89
|
+
|
90
|
+
class BigDecimal
|
91
|
+
def duplicable?
|
92
|
+
true
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class Method
|
97
|
+
# Methods are not duplicable:
|
98
|
+
#
|
99
|
+
# method(:puts).duplicable? # => false
|
100
|
+
# method(:puts).dup # => TypeError: allocator undefined for Method
|
101
|
+
def duplicable?
|
102
|
+
false
|
103
|
+
end
|
104
|
+
end
|
data/lib/wedge/utilis/hash.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require_relative 'duplicable'
|
2
|
+
|
1
3
|
class Hash
|
2
4
|
# add keys to hash
|
3
5
|
def to_obj
|
@@ -103,3 +105,49 @@ class HashObject
|
|
103
105
|
Wedge::IndifferentHash.new self.to_h.inject({}) {|copy, (key, value)| copy[key] = value.dup rescue value; copy}
|
104
106
|
end
|
105
107
|
end
|
108
|
+
|
109
|
+
class Object
|
110
|
+
# Returns a deep copy of object if it's duplicable. If it's
|
111
|
+
# not duplicable, returns +self+.
|
112
|
+
#
|
113
|
+
# object = Object.new
|
114
|
+
# dup = object.deep_dup
|
115
|
+
# dup.instance_variable_set(:@a, 1)
|
116
|
+
#
|
117
|
+
# object.instance_variable_defined?(:@a) # => false
|
118
|
+
# dup.instance_variable_defined?(:@a) # => true
|
119
|
+
def deep_dup
|
120
|
+
duplicable? ? dup : self
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class Array
|
125
|
+
# Returns a deep copy of array.
|
126
|
+
#
|
127
|
+
# array = [1, [2, 3]]
|
128
|
+
# dup = array.deep_dup
|
129
|
+
# dup[1][2] = 4
|
130
|
+
#
|
131
|
+
# array[1][2] # => nil
|
132
|
+
# dup[1][2] # => 4
|
133
|
+
def deep_dup
|
134
|
+
map(&:deep_dup)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
class Hash
|
139
|
+
# Returns a deep copy of hash.
|
140
|
+
#
|
141
|
+
# hash = { a: { b: 'b' } }
|
142
|
+
# dup = hash.deep_dup
|
143
|
+
# dup[:a][:c] = 'c'
|
144
|
+
#
|
145
|
+
# hash[:a][:c] # => nil
|
146
|
+
# dup[:a][:c] # => "c"
|
147
|
+
def deep_dup
|
148
|
+
each_with_object(dup) do |(key, value), hash|
|
149
|
+
hash.delete(key)
|
150
|
+
hash[key.deep_dup] = value.deep_dup
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
data/lib/wedge/version.rb
CHANGED
data/playground/app/app.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
class Playground
|
2
2
|
use Rack::Session::Cookie, secret: APP_SECRET
|
3
|
+
# gzips assets
|
4
|
+
use Rack::Deflater, include: %w{text/plain application/xml application/json application/javascript text/css text/json}
|
3
5
|
|
6
|
+
plugin :middleware
|
4
7
|
plugin :environments
|
5
8
|
|
6
9
|
configure :development do
|
@@ -13,17 +16,26 @@ class Playground
|
|
13
16
|
BetterErrors.application_root = Dir.pwd
|
14
17
|
end
|
15
18
|
|
16
|
-
plugin :
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
19
|
+
plugin :csrf, skip_if: ->(_) { RACK_ENV == 'test' }
|
20
|
+
plugin :wedge do |c|
|
21
|
+
c.scope = Playground
|
22
|
+
c.debug = true
|
23
|
+
c.app_dir = RACK_ENV != 'test' ? 'app' : 'playground/app'
|
24
|
+
c.plugin :form
|
25
|
+
c.plugin :current_user, client_fields: %w'id first_name last_name is_admin' do
|
26
|
+
User.find(1)
|
27
|
+
end
|
28
|
+
# c.plugin_settings :uploader, {
|
29
|
+
# aws_access_key_id: AWS_ACCESS_KEY_ID,
|
30
|
+
# aws_secret_access_key: AWS_SECRET_ACCESS_KEY,
|
31
|
+
# bucket: AWS_BUCKET
|
32
|
+
# }
|
33
|
+
c.settings[:uploader] = {
|
34
|
+
aws_access_key_id: AWS_ACCESS_KEY_ID,
|
35
|
+
aws_secret_access_key: AWS_SECRET_ACCESS_KEY,
|
36
|
+
bucket: AWS_BUCKET
|
25
37
|
}
|
26
|
-
|
38
|
+
end
|
27
39
|
|
28
40
|
# builder = Opal::Builder.new(:stubs=>['opal'])
|
29
41
|
# builder.append_paths(APP_ROOT)
|
@@ -49,7 +61,7 @@ class Playground
|
|
49
61
|
}
|
50
62
|
|
51
63
|
route do |r|
|
52
|
-
r.wedge_assets
|
64
|
+
# r.wedge_assets
|
53
65
|
r.assets
|
54
66
|
|
55
67
|
r.root do
|
@@ -59,5 +71,9 @@ class Playground
|
|
59
71
|
r.on 'uploader' do
|
60
72
|
wedge(:uploader).to_js :display
|
61
73
|
end
|
74
|
+
|
75
|
+
r.on 'todo_list' do
|
76
|
+
wedge(:todo_list).to_js :display
|
77
|
+
end
|
62
78
|
end
|
63
79
|
end
|
@@ -29,9 +29,14 @@ class Playground
|
|
29
29
|
def display options = {}, &block
|
30
30
|
return unless server?
|
31
31
|
|
32
|
+
begin; dom.find('head').add_child csrf_metatag; rescue; end
|
32
33
|
body_dom = dom.find('body')
|
33
34
|
body_dom << block.call if block_given?
|
34
35
|
|
36
|
+
if body_class = options[:body_class]
|
37
|
+
body_dom.add_class body_class
|
38
|
+
end
|
39
|
+
|
35
40
|
dom
|
36
41
|
end
|
37
42
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative '../forms/todo_list_add'
|
2
|
+
|
3
|
+
class Playground
|
4
|
+
class TodoListComponent < Wedge::Component
|
5
|
+
name :todo_list
|
6
|
+
html 'public/todo_list.html'
|
7
|
+
|
8
|
+
def display
|
9
|
+
wedge(:layout).display(body_class: 'todo_list') { dom } if server?
|
10
|
+
end
|
11
|
+
|
12
|
+
on :submit, '#add-form', key: :todo, form: :todo_list_add do |form, el|
|
13
|
+
button = el.find('button[type="submit"]')
|
14
|
+
button.prop("disabled", true)
|
15
|
+
if form.valid?
|
16
|
+
puts 'valid'
|
17
|
+
else
|
18
|
+
form.display_errors
|
19
|
+
button.prop("disabled", false)
|
20
|
+
puts 'errors'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -17,3 +17,6 @@ require 'app'
|
|
17
17
|
|
18
18
|
Dir["#{APP_ROOT}/forms/*.rb"].sort.each { |file| require file }
|
19
19
|
Dir["#{APP_ROOT}/components/*.rb"].sort.each { |file| require file }
|
20
|
+
Dir["#{APP_ROOT}/models/*.rb"].sort.each { |file| require file }
|
21
|
+
|
22
|
+
require './spec/spec_helper' if RACK_ENV == 'test'
|