wedge 0.1.17 → 0.1.18
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/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'
|