wedge 0.1.17 → 0.1.18

Sign up to get free protection for your applications and to get access to all the features.
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)