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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: af418ce6886e6fa3eee260087428493516a32f0f
4
- data.tar.gz: 839e40d9c95003e142ac074f11b5af0a68c1a4b4
3
+ metadata.gz: 3d5cba344b9b2d2cb5555b9eaf30132afd576c85
4
+ data.tar.gz: 3f13558b89e8a9d960b417f7e792b96385e56992
5
5
  SHA512:
6
- metadata.gz: a449dcbb6616c22a21b98aef2097970910d8be80fb257a220b6ff6c14636d2611034102a29859f8b756edade7784a35b9543eed598528c2b5ed4acd56ca15e24
7
- data.tar.gz: 73e9ac6539b6949cb1f44dd639b671d8ead67ed6bdc3874086231b5dd207ec3b124d03b9f2ed2d5d2fcdd8bec9ee55f80b58cd343157270444f7f1bce37105e8
6
+ metadata.gz: 9d5d9ebb80b5aaf32d7bf2fa688176e34b45cfadb39eabdd58c5863351abbd0b5bfc1918ebf98dcd021d47a640742bac0c63fe566b7446fee3e55ea9c79e387b
7
+ data.tar.gz: 428783e6058d34db64e503538d29851d04274570db989afa3775daf5b8d48148aa74bb2e229eab776102b99b06be09c89e0e35fb8e1e50b9c773834886b1d3bc
data/Gemfile CHANGED
@@ -5,10 +5,17 @@ gemspec
5
5
 
6
6
  gem 'opal', github: 'opal/opal'
7
7
  gem 'opal-jquery', github: 'opal/opal-jquery'
8
- gem 'opal-rspec', github: 'opal/opal-rspec'
8
+ gem 'opal-rspec-cj'
9
9
  gem 'binding_of_caller'
10
10
  gem 'better_errors'
11
11
  gem 'slim'
12
12
  gem 'sass'
13
13
  gem 'thin'
14
+ gem 'roda', '2.4.0'
15
+ gem 'pry-rescue'
16
+ gem 'pry-stack_explorer'
17
+ # issue: https://github.com/copiousfreetime/hitimes/issues/17
18
+ gem 'hitimes', '1.2.1'
19
+ gem 'restart'
20
+ gem 'rack_csrf', '2.5.0'
14
21
  gem 'roda-bin'
data/Makefile ADDED
@@ -0,0 +1,2 @@
1
+ server:
2
+ cd playground && bundle exec restart -d ../. -i \.bundle$$ -i \.git$$ thin start -p 8080
data/Rakefile CHANGED
@@ -1,19 +1,23 @@
1
1
  $:.unshift(File.expand_path('./lib'))
2
2
  $:.unshift(File.expand_path("./playground/app"))
3
3
 
4
+ current_task = Rake.application.top_level_tasks.first
5
+
6
+ ENV['RACK_ENV'] ||= (current_task['default'] || current_task[/(rspec|test)\z/]) ? 'test' : 'development'
7
+
4
8
  require 'bundler'
5
9
  require 'bundler/gem_tasks'
6
10
  require 'bundler/setup'
7
11
 
8
12
  require 'config/boot'
9
- require 'opal'
13
+ require 'tilt/erubis'
10
14
  require 'opal/rspec/rake_task'
11
15
 
12
16
  Opal.append_path File.expand_path('../lib', __FILE__)
13
17
  Opal.append_path File.expand_path('../playground/app', __FILE__)
14
18
  Opal.append_path File.expand_path('../playground/public', __FILE__)
15
19
 
16
- Opal::RSpec::RakeTask.new('opal:rspec') do |s|
20
+ Opal::RSpec::RakeTask.new('opal:rspec', Playground) do |s|
17
21
  s.index_path = 'spec/index.html.erb'
18
22
  end
19
23
 
@@ -23,8 +27,8 @@ require 'rspec/core/rake_task'
23
27
  RSpec::Core::RakeTask.new('ruby:rspec')
24
28
 
25
29
  task :test do
30
+ puts "--------------------------\nRun specs in Ruby\n--------------------------"
31
+ Rake::Task['ruby:rspec'].invoke
26
32
  puts "--------------------------\nRun specs in Opal\n--------------------------"
27
33
  Rake::Task['opal:rspec'].invoke
28
- puts "--------------------------\nRun specs in normal ruby\n--------------------------"
29
- Rake::Task['ruby:rspec'].invoke
30
34
  end
data/TODO.md ADDED
@@ -0,0 +1,4 @@
1
+ ### Todo
2
+ - Cleanup plugin settings and make a plugin\_setting/settings method
3
+ - Make it so the wedge config isn't passed down to all new components
4
+ - more tests!
@@ -1,121 +1,39 @@
1
1
  class Roda
2
2
  module RodaPlugins
3
- class Wedge
4
- def self.configure(app, opts = {})
5
- if app.opts[:wedge]
6
- app.opts[:wedge].merge!(opts)
3
+ class WedgePlugin
4
+ def self.configure(app, opts = false, &block)
5
+ if !opts || !opts.delete(:disable_middleware)
6
+ app.use Wedge::Middleware, opts || block
7
7
  else
8
- app.opts[:wedge] = opts.dup
8
+ opts.each { |k, v| Wedge.config.send "#{k}=", v }
9
9
  end
10
+ end
10
11
 
11
- opts = app.opts[:wedge]
12
-
13
- opts.each do |k, v|
14
- case k.to_s
15
- when 'plugins'
16
- v.each { |p| ::Wedge.config.plugin p }
17
- when 'scope'
18
- begin
19
- ::Wedge.config.scope = v.new
20
- rescue
21
- ::Wedge.config.scope = v.new({})
22
- end
23
- else
24
- ::Wedge.config.send "#{k}=", v
25
- end
12
+ module ClassMethods
13
+ def wedge_plugin name, settings = {}, &block
14
+ Wedge.plugin name, settings, &block
26
15
  end
27
-
28
- # cache the javascript on load
29
- ::Wedge.javascript if opts[:cache_assets]
30
16
  end
31
17
 
32
18
  module InstanceMethods
33
19
  def wedge(name, *args, &block)
34
- ::Wedge.scope!(self)[name, *args, &block]
20
+ Wedge.scope!(self)[name, *args, &block]
35
21
  end
36
22
 
37
23
  def wedge_plugin(name, *args, &block)
38
- ::Wedge.scope!(self)["#{name}_plugin", *args, &block]
39
- end
40
- end
41
-
42
- module RequestClassMethods
43
- def wedge_route_regex
44
- assets_url = ::Wedge.assets_url.gsub(%r{^\/}, '')
45
- # # We also allow for no assets key so when we post server methods there
46
- # # isn't an error if the key has been changed since a browser refresh.
47
- %r{(?:#{assets_url}|#{assets_url.sub("#{::Wedge.config.assets_key}/", '')})/(.*)\.(.*)$}
24
+ Wedge.scope!(self)["#{name}_plugin", *args, &block]
48
25
  end
49
26
  end
50
27
 
51
28
  module RequestMethods
52
29
  def wedge_assets
53
- on self.class.wedge_route_regex do |component, ext|
54
- case ext
55
- when 'map'
56
- ::Wedge.source_map component
57
- when 'rb'
58
- if component =~ /^wedge/
59
- path = ::Wedge.config.path.gsub(/\/wedge.rb$/, '')
60
- File.read("#{path}/#{component}.rb")
61
- else
62
- File.read("#{component}.rb")
63
- end
64
- when 'call'
65
- body = scope.request.body.read
66
- data = scope.request.params
67
-
68
- begin
69
- # try json
70
- data.merge!(body ? JSON.parse(body) : {})
71
- rescue
72
- begin
73
- # try form data
74
- data.merge!(body ? Rack::Utils.parse_query(body) : {})
75
- rescue
76
- # no data
77
- end
78
- end
79
-
80
- data = data.indifferent
81
- name = data.delete(:__wedge_name__)
82
- method_called = data.delete(:__wedge_method__)
83
- method_args = data.delete(:__wedge_args__)
84
-
85
- if method_args == '__wedge_data__' && data
86
- method_args = [data]
87
- res = scope.wedge(name).send(method_called, *method_args) || ''
88
- else
89
- # This used to send things like init, we need a better way to
90
- # send client config data to the server
91
- # res = scope.wedge(name, data).send(method_called, *method_args) || ''
92
- res = scope.wedge(name).send(method_called, *method_args) || ''
93
- end
94
-
95
- scope.response.headers["WEDGE-CSRF-TOKEN"] = scope.csrf_token if scope.methods.include? :csrf_token
96
-
97
- if res.is_a? Hash
98
- scope.response.headers["Content-Type"] = 'application/json; charset=UTF-8'
99
- res = res.to_json
100
- else
101
- res = res.to_s
102
- end
103
-
104
- res
105
- else
106
- scope.response.headers['Content-Type'] = 'application/javascript; charset=UTF-8'
107
-
108
- if ::Wedge.config.debug
109
- "#{::Wedge.javascript(component)}\n//# sourceMappingURL=/assets/wedge/#{component}.map"
110
- else
111
- ::Wedge.javascript(component)
112
- end
113
- end
30
+ on Wedge.assets_url_regex do
31
+ run @@__wedge_middleware__ ||= Wedge::Middleware.scope!(scope).new
114
32
  end
115
33
  end
116
34
  end
117
35
  end
118
36
 
119
- register_plugin(:wedge, Wedge)
37
+ register_plugin(:wedge, WedgePlugin)
120
38
  end
121
39
  end
data/lib/wedge.rb CHANGED
@@ -8,6 +8,7 @@ require 'wedge/utilis/try'
8
8
  require 'wedge/utilis/titleize'
9
9
  require 'wedge/utilis/element'
10
10
  require 'base64'
11
+ require 'forwardable'
11
12
  unless RUBY_ENGINE == 'opal'
12
13
  require 'nokogiri'
13
14
  require 'wedge/utilis/nokogiri'
@@ -23,10 +24,14 @@ class Wedge
23
24
  include Methods
24
25
 
25
26
  class << self
27
+ extend Forwardable
28
+
26
29
  ATTR_ACCESSORS = %i{scope store config events}
27
30
 
28
31
  attr_accessor(*ATTR_ACCESSORS)
29
32
 
33
+ delegate [:plugin] => :config
34
+
30
35
  def assets_url
31
36
  url = config.assets_url.gsub(%r{^(http(|s)://[^\/]*\/|\/)}, '/')
32
37
  "#{url}#{config.cache_assets ? "/#{config.assets_key}" : ''}"
@@ -91,17 +96,17 @@ class Wedge
91
96
  #
92
97
  # @param name [String, Symbol, #to_s] The unique name given to a component.
93
98
  # @return [Wedge::Component#method] Last line of the method called.
94
- def [](name, *args, &block)
95
- config.component_class[name].wedge_new self, *args, &block
99
+ def [](*args, &block)
100
+ config.component_class[args.shift].wedge_new self, *args, &block
96
101
  end
97
102
 
98
103
  %w(store scope).each do |meth|
99
104
  define_method "#{meth}!" do |value|
100
105
  klass = Class.new(self)
101
106
  ATTR_ACCESSORS.each do |name|
102
- klass.instance_variable_set(:"@#{name}", Wedge.instance_variable_get(:"@#{name}"))
107
+ klass.instance_variable_set(:"@#{name}", Wedge.instance_variable_get(:"@#{name}").deep_dup)
103
108
  end
104
- klass.instance_variable_set(:"@#{meth}", value)
109
+ klass.instance_variable_set(:"@#{meth}", value.deep_dup)
105
110
  klass
106
111
  end
107
112
  end
@@ -32,6 +32,10 @@ class Wedge
32
32
  obj
33
33
  end
34
34
 
35
+ def plugin *args
36
+ Wedge.plugin *args
37
+ end
38
+
35
39
  alias_method :original_name, :name
36
40
  def wedge_name(*args)
37
41
  if args.any?
@@ -113,7 +117,7 @@ class Wedge
113
117
  alias_method :dom, :wedge_dom
114
118
 
115
119
  def wedge_config
116
- @wedge_config ||= Config.new Wedge.config.data.dup.merge(klass: self)
120
+ @wedge_config ||= Config.new klass: self, scope: Wedge.config.scope
117
121
  end
118
122
  alias_method :config, :wedge_config
119
123
 
@@ -181,25 +185,50 @@ class Wedge
181
185
  # refreshed the browser yet.
182
186
  call_url = "#{Wedge.assets_url.sub("#{Wedge.config.assets_key}/",'')}/#{path_name}.call"
183
187
 
184
- HTTP.post(call_url,
185
- headers: {
186
- 'X-CSRF-TOKEN' => Element.find('meta[name=_csrf]').attr('content'),
187
- 'X-WEDGE-METHOD-REQUEST' => true
188
- },
189
- payload: payload) do |response|
190
-
191
- # We set the new csrf token
192
- xhr = Native(response.xhr)
193
- csrf = xhr.getResponseHeader('WEDGE-CSRF-TOKEN')
194
- Element.find('meta[name=_csrf]').attr 'content', csrf
195
- ###########################
196
-
197
- res = JSON.from_object(`response`)
198
-
199
- blk.call res[:body], res
188
+ if block_given?
189
+ HTTP.post(call_url,
190
+ headers: {
191
+ 'X-CSRF-TOKEN' => Element.find('meta[name=_csrf]').attr('content'),
192
+ 'X-WEDGE-METHOD-REQUEST' => meth
193
+ },
194
+ async: false,
195
+ payload: payload) do |response|
196
+
197
+ # We set the new csrf token
198
+ xhr = Native(response.xhr)
199
+ # discuss: I don't think we should update the csrf token every ajax call
200
+ # csrf = xhr.getResponseHeader('WEDGE-CSRF-TOKEN')
201
+ # Element.find('meta[name=_csrf]').attr 'content', csrf
202
+ ###########################
203
+
204
+ res = JSON.from_object(`response`)
205
+
206
+ blk.call res[:body], res
207
+ end
208
+ else
209
+ data = {
210
+ headers: {
211
+ 'X-CSRF-TOKEN' => "#{Element.find('meta[name=_csrf]').attr('content')}",
212
+ 'X-WEDGE-METHOD-REQUEST' => meth
213
+ },
214
+ dataType: 'json',
215
+ type: 'POST',
216
+ url: call_url,
217
+ data: payload,
218
+ async: false
219
+ }.to_n
220
+
221
+ response = `$.ajax(data).responseText`
222
+ begin
223
+ JSON.parse response
224
+ rescue
225
+ if response.empty?
226
+ raise "Ajax response to #{call_url} was empty."
227
+ else
228
+ puts response
229
+ end
230
+ end
200
231
  end
201
-
202
- true
203
232
  end
204
233
  end
205
234
 
@@ -220,14 +249,17 @@ class Wedge
220
249
  end
221
250
  end
222
251
 
223
- if RUBY_ENGINE == 'opal'
224
- def wedge(*args)
225
- Wedge[*args]
226
- end
252
+ # We want the scope to override this method if defined
253
+ def wedge(*args, &block)
254
+ # fix: can't pass block to Wedge, opal error:
255
+ # https://github.com/opal/opal/issues/959
256
+ # scope.respond_to?(:wedge) ? scope.wedge(*args, &block) : Wedge[*args, &block]
257
+ scope.respond_to?(:wedge) ? scope.wedge(*args, &block) : Wedge[*args]
258
+ end
227
259
 
228
- def wedge_plugin(name, *args, &block)
229
- wedge("#{name}_plugin", *args, &block)
230
- end
260
+ # We want the scope to override this method if defined
261
+ def wedge_plugin(name, *args, &block)
262
+ scope.respond_to?(:wedge_plugin) ? scope.wedge_plugin(*args, &block) : wedge("#{name}_plugin", *args, &block)
231
263
  end
232
264
 
233
265
  def wedge_scope
@@ -240,10 +272,15 @@ class Wedge
240
272
  end
241
273
  alias_method :store, :wedge_store
242
274
 
275
+ def wedge_class_store
276
+ self.class.wedge_config.store
277
+ end
278
+ alias_method :class_store, :wedge_class_store
279
+
243
280
  # Duplicate of class condig [Config]
244
281
  # @return config [Config]
245
282
  def wedge_config
246
- @wedge_config ||= Config.new(self.class.wedge_config.data.dup)
283
+ @wedge_config ||= Config.new(self.class.wedge_config.data.deep_dup)
247
284
  end
248
285
  alias_method :config, :wedge_config
249
286
 
@@ -294,12 +331,16 @@ class Wedge
294
331
  alias_method :function, :wedge_function
295
332
 
296
333
  def wedge_from_server?
297
- !scope.respond_to?(:request) || (request && !request.env.include?('HTTP_X_WEDGE_METHOD_REQUEST'))
334
+ begin; !request.try(:env).include?('HTTP_X_WEDGE_METHOD_REQUEST'); rescue; true end
298
335
  end
299
336
  alias_method :from_server?, :wedge_from_server?
300
337
 
301
338
  def wedge_from_client?
302
- !wedge_from_server?
339
+ begin
340
+ caller_locations(1,1)[0].label == request.try(:env)['HTTP_X_WEDGE_METHOD_REQUEST']
341
+ rescue
342
+ false
343
+ end
303
344
  end
304
345
  alias_method :from_client?, :wedge_from_client?
305
346
 
data/lib/wedge/config.rb CHANGED
@@ -16,12 +16,14 @@ class Wedge
16
16
  path: nil,
17
17
  html: nil,
18
18
  scope: nil,
19
+ block: nil,
19
20
  debug: false,
20
21
  app_dir: 'app',
21
22
  assets_url: '/assets/wedge',
22
23
  assets_key: nil,
23
24
  cache_assets: false,
24
25
  is_plugin: false,
26
+ compile_str: false,
25
27
  requires: IndifferentHash.new,
26
28
  triggered_browser_events: false,
27
29
  store: IndifferentHash.new,
@@ -41,17 +43,45 @@ class Wedge
41
43
  @data.dup.select {|k, v| allowed_client_data.include? k }
42
44
  end
43
45
 
44
- def plugin(name)
46
+ def plugin(name, settings = {}, &block)
47
+ plugin_name = "#{name}_plugin"
48
+
49
+ Wedge.config.settings[plugin_name] = settings
50
+
45
51
  unless RUBY_ENGINE == 'opal'
46
52
  require "wedge/plugins/#{name}"
47
53
  end
48
54
 
49
- klass = Wedge.config.component_class[:"#{name}_plugin"]
50
- plugins << klass.config.path unless plugins.include? klass.config.path
55
+ klass = Wedge.config.component_class[plugin_name]
56
+
57
+ unless plugins.include? klass.config.path
58
+ klass.config.settings = settings
59
+ klass.config.block = block
60
+ klass.config.is_plugin = true
51
61
 
52
- # Merge in instance/class methods
53
- Wedge::Component.send(:include, klass::InstanceMethods) if defined?(klass::InstanceMethods)
54
- Wedge::Component.extend(klass::ClassMethods) if defined?(klass::ClassMethods)
62
+ plugins << klass.config.path
63
+ plugins.uniq!
64
+
65
+ # Merge in instance/class methods
66
+ Wedge::Component.send(:include, klass::InstanceMethods) if defined?(klass::InstanceMethods)
67
+ Wedge::Component.extend(klass::ClassMethods) if defined?(klass::ClassMethods)
68
+ end
69
+ end
70
+
71
+ def plugins= plugins
72
+ plugins.each { |p| plugin(p.to_s) }
73
+ end
74
+
75
+ def scope= value
76
+ if value.respond_to? :new
77
+ begin
78
+ @data.scope = value.new
79
+ rescue
80
+ @data.scope = value.new({})
81
+ end
82
+ else
83
+ @data.scope = value
84
+ end
55
85
  end
56
86
 
57
87
  def method_missing(method, *args, &block)