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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +8 -1
  3. data/Makefile +2 -0
  4. data/Rakefile +8 -4
  5. data/TODO.md +4 -0
  6. data/lib/roda/plugins/wedge.rb +14 -96
  7. data/lib/wedge.rb +9 -4
  8. data/lib/wedge/component.rb +70 -29
  9. data/lib/wedge/config.rb +36 -6
  10. data/lib/wedge/middleware.rb +43 -35
  11. data/lib/wedge/opal.rb +15 -4
  12. data/lib/wedge/plugins/ability_list.rb +95 -0
  13. data/lib/wedge/plugins/current_user.rb +48 -0
  14. data/lib/wedge/plugins/factory.rb +36 -0
  15. data/lib/wedge/plugins/form.rb +216 -343
  16. data/lib/wedge/plugins/{validations.rb → form/validations.rb} +64 -37
  17. data/lib/wedge/plugins/form_backup.rb +442 -0
  18. data/lib/wedge/plugins/render.rb +110 -0
  19. data/lib/wedge/plugins/uploader.rb +2 -2
  20. data/lib/wedge/utilis/duplicable.rb +104 -0
  21. data/lib/wedge/utilis/hash.rb +48 -0
  22. data/lib/wedge/version.rb +1 -1
  23. data/playground/app/app.rb +27 -11
  24. data/playground/app/components/abilities.rb +11 -0
  25. data/playground/app/components/current_user.rb +12 -0
  26. data/playground/app/components/layout.rb +5 -0
  27. data/playground/app/components/todo_list.rb +24 -0
  28. data/playground/app/config/boot.rb +3 -0
  29. data/playground/app/forms/todo_list_add.rb +9 -0
  30. data/playground/app/models/user.rb +5 -0
  31. data/playground/public/css/styles.css +139 -0
  32. data/playground/public/css/todo_list.css +138 -0
  33. data/playground/public/todo_list.html +23 -0
  34. data/playground/src/css/styles.scss +2 -1
  35. data/playground/src/css/todo_list.scss +165 -0
  36. data/playground/src/todo_list.slim +17 -0
  37. data/spec/playground/uploader_spec.rb +27 -29
  38. data/spec/spec_helper.rb +29 -0
  39. data/spec/stubs/models/user.rb +15 -0
  40. data/spec/wedge/plugins/current_user_spec.rb +29 -0
  41. data/spec/wedge/plugins/factory_spec.rb +16 -0
  42. data/spec/wedge/plugins/form_spec.rb +119 -0
  43. data/spec/wedge/plugins/uploader_spec.rb +1 -3
  44. data/spec/wedge_spec.rb +3 -3
  45. 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
- client? ? %w(aws_access_key_id bucket).include?(k) : true
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  class Wedge
2
- VERSION = '0.1.17'
2
+ VERSION = '0.1.18'
3
3
  end
@@ -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 :wedge, {
17
- scope: self,
18
- plugins: [:form],
19
- settings: {
20
- uploader: {
21
- aws_access_key_id: AWS_ACCESS_KEY_ID,
22
- aws_secret_access_key: AWS_SECRET_ACCESS_KEY,
23
- bucket: AWS_BUCKET
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
@@ -0,0 +1,11 @@
1
+ class Playground
2
+ class Abilities < Wedge::Plugins::AbilityList
3
+ name :ability_list
4
+
5
+ def initialize user
6
+ if user.admin?
7
+ can :view, :admin
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ class Playground
2
+ class CurrentUserComponent < Wedge::Plugins::CurrentUser
3
+ name :current_user
4
+
5
+ attr_reader :id, :is_admin, :secret_key
6
+ attr_accessor :first_name, :last_name
7
+
8
+ def admin?
9
+ is_admin
10
+ end
11
+ end
12
+ 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'
@@ -0,0 +1,9 @@
1
+ class TodoListAddForm < Wedge::Plugins::Form
2
+ name :todo_list_add
3
+
4
+ attr_accessor :description
5
+
6
+ def validate
7
+ assert_present :description
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ class Playground
2
+ class User
3
+
4
+ end
5
+ end