wedgeio 0.0.1

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.
@@ -0,0 +1,321 @@
1
+ module Wedge
2
+ class Component
3
+ include Methods
4
+
5
+ REJECTED_CLIENT_OPTS = %i(scope file_path methods_wrapped events klass on on_server_methods added_class_events loaded html)
6
+
7
+ class << self
8
+ # Override the default new behaviour
9
+ def new(*args, &block)
10
+ obj = allocate
11
+
12
+ obj.wedge_opts.js = args.delete(:js)
13
+ obj.wedge_opts.init = args.delete(:init)
14
+
15
+ # Merge other args into opts
16
+ args.each { |a| a.each {|k, v| obj.wedge_opts[k] = v } } if args.any?
17
+
18
+ obj.wedge_opts.events.scope = obj
19
+
20
+ # Set all the on events
21
+ obj.wedge_opts.on.each do |*a, &b|
22
+ obj.wedge_opts.events.add(*a.first.first, &a.first.last)
23
+ end
24
+ wedge_opts.added_class_events = true
25
+
26
+ if obj.wedge_opts.init
27
+ if obj.wedge_opts.init.is_a? Array
28
+ obj.send :initialize, *obj.wedge_opts.init, &block
29
+ else
30
+ obj.send :initialize, obj.wedge_opts.init, &block
31
+ end
32
+ else
33
+ obj.send :initialize, &block
34
+ end
35
+
36
+ unless wedge_opts.methods_wrapped
37
+ obj.wedge_opts.methods_wrapped = wedge_opts.methods_wrapped = true
38
+
39
+ public_instance_methods(false).each do |meth|
40
+ alias_method :"wedge_original_#{meth}", :"#{meth}"
41
+ define_method "#{meth}" do |*d_args, &blk|
42
+ if server? && !wedge_opts.method_called && wedge_opts.js
43
+ wedge_opts.method_called = meth
44
+ wedge_opts.method_args = *d_args
45
+ end
46
+
47
+ o_name = "wedge_original_#{meth}"
48
+
49
+ if client? || method(o_name).parameters.length > 0
50
+ result = send(o_name, *d_args, &blk)
51
+ else
52
+ result = send(o_name, &blk)
53
+ end
54
+
55
+ # Append the initialize javscript
56
+ if server? && opts.js
57
+ result = result.to_html if result.is_a? DOM
58
+ result << wedge_javascript if result.is_a? String
59
+ end
60
+
61
+ result
62
+ end
63
+ end
64
+ end
65
+
66
+ obj
67
+ end
68
+
69
+ # Used to setup the component with default options.
70
+ #
71
+ # @example
72
+ # class SomeComponent < Component
73
+ # setup do |config|
74
+ # config.name :some
75
+ # end
76
+ # end
77
+ # @yield [Config]
78
+ def wedge_setup(&block)
79
+ block.call wedge_config
80
+ end
81
+ alias_method :setup, :wedge_setup
82
+
83
+ # Set templates
84
+ #
85
+ # @example
86
+ # tmpl :some_name, dom.find('#some-div')
87
+ # @return dom [DOM]
88
+ def wedge_tmpl(name, dom = false, remove = true)
89
+ if dom
90
+ dom = remove ? dom.remove : dom
91
+ wedge_opts.tmpl[name] = {
92
+ dom: dom,
93
+ html: dom.to_html
94
+ }
95
+ elsif t = wedge_opts.tmpl[name]
96
+ dom = DOM.new t[:html]
97
+ else
98
+ false
99
+ end
100
+
101
+ dom
102
+ end
103
+ alias_method :tmpl, :wedge_tmpl
104
+
105
+ def wedge_dom
106
+ @wedge_dom ||= DOM.new wedge_opts.html
107
+ end
108
+ alias_method :dom, :wedge_dom
109
+
110
+ # Shortcut for Wedge.components
111
+ #
112
+ # @return [Hash, Wedge.components]
113
+ def wedge_components
114
+ Wedge.components ||= {}
115
+ end
116
+ alias_method :components, :wedge_components
117
+
118
+ # Shortcut for the Config#opts
119
+ #
120
+ # @return [Openstruct, Config#opts]
121
+ def wedge_opts
122
+ wedge_config.opts
123
+ end
124
+ alias_method :opts, :wedge_opts
125
+
126
+ def wedge_config
127
+ @wedge_config ||= begin
128
+ args = Wedge.config.opts_dup.merge(klass: self, object_events: {})
129
+
130
+ unless RUBY_ENGINE == 'opal'
131
+ args[:file_path] = caller.first.gsub(/(?<=\.rb):.*/, '')
132
+ args[:path_name] = args[:file_path]
133
+ .gsub(%r{(#{Dir.pwd}/|.*(?=wedge))}, '')
134
+ .gsub(/\.rb$/, '')
135
+ end
136
+
137
+ c = Config.new(args)
138
+
139
+ # If extending from a plugin it will automatically require it.
140
+ ancestors.each do |klass|
141
+ next if klass.to_s == name.to_s
142
+
143
+ if klass.method_defined?(:wedge_opts) && klass.wedge_opts.name.to_s =~ /_plugin$/
144
+ c.requires klass.wedge_opts.name
145
+ end
146
+ end
147
+
148
+ c
149
+ end
150
+ end
151
+ alias_method :config, :wedge_config
152
+
153
+ def wedge_on(*args, &block)
154
+ if args.first.to_s != 'server'
155
+ wedge_opts.on << [args, block]
156
+ else
157
+ wedge_on_server(&block)
158
+ end
159
+ end
160
+ alias_method :on, :wedge_on
161
+
162
+ def method_missing(method, *args, &block)
163
+ if wedge_opts.scope.respond_to?(method, true)
164
+ wedge_opts.scope.send method, *args, &block
165
+ else
166
+ super
167
+ end
168
+ end
169
+
170
+ def client_wedge_opts
171
+ wedge_config.opts_dup.reject {|k, v| REJECTED_CLIENT_OPTS.include? k }
172
+ end
173
+
174
+ def wedge_on_server(&block)
175
+ if server?
176
+ yield
177
+ else
178
+ m = Module.new(&block)
179
+
180
+ m.public_instance_methods(false).each do |meth|
181
+ wedge_opts.on_server_methods << meth.to_s
182
+
183
+ define_method "#{meth}" do |*args, &blk|
184
+ path_name = wedge_opts.path_name
185
+ # event_id = "comp-event-#{$faye.generate_id}"
186
+
187
+ payload = client_wedge_opts.reject do |k, _|
188
+ %w(html tmpl requires plugins object_events js_loaded).include? k
189
+ end
190
+ payload[:method_called] = meth
191
+ payload[:method_args] = args
192
+
193
+ HTTP.post("/#{wedge_opts.assets_url}/#{path_name}.call",
194
+ headers: {
195
+ 'X-CSRF-TOKEN' => Element.find('meta[name=_csrf]').attr('content')
196
+ },
197
+ payload: payload) do |response|
198
+
199
+ # We set the new csrf token
200
+ xhr = Native(response.xhr)
201
+ csrf = xhr.getResponseHeader('BIO-CSRF-TOKEN')
202
+ Element.find('meta[name=_csrf]').attr 'content', csrf
203
+ ###########################
204
+
205
+ res = JSON.from_object(`response`)
206
+
207
+ blk.call res[:body], res
208
+ end
209
+
210
+ true
211
+ end
212
+ end
213
+
214
+ include m
215
+ end
216
+ end
217
+ end
218
+
219
+ # Duplicate of class condig [Config]
220
+ # @return config [Config]
221
+ def wedge_config
222
+ @wedge_config ||= begin
223
+ c = Config.new(self.class.wedge_config.opts_dup.merge(events: Events.new))
224
+ c.opts.events.object_events = c.opts.object_events.dup
225
+ c.opts.object_events = {}
226
+ c
227
+ end
228
+ end
229
+ alias_method :config, :wedge_config
230
+
231
+ # Duplicated of config.opts [Config#opts]
232
+ # @return opts [Config#opts]
233
+ def wedge_opts
234
+ wedge_config.opts
235
+ end
236
+ alias_method :opts, :wedge_opts
237
+
238
+ # Grab a copy of the template
239
+ # @return dom [DOM]
240
+ def wedge_tmpl(name)
241
+ self.class.wedge_tmpl name
242
+ end
243
+ alias_method :tmpl, :wedge_tmpl
244
+
245
+ # Dom
246
+ # @return wedge_dom [Dom]
247
+ def wedge_dom
248
+ @wedge_dom ||= begin
249
+ if server?
250
+ DOM.new self.class.wedge_dom.to_html
251
+ else
252
+ DOM.new(Element)
253
+ end
254
+ end
255
+ end
256
+ alias_method :dom, :wedge_dom
257
+
258
+ # Special method that acts like the javascript equivalent
259
+ # @example
260
+ # foo = {
261
+ # bar: function { |moo|
262
+ # moo.call 'something'
263
+ # }
264
+ # }.to_n
265
+ def wedge_function(*args, &block)
266
+ args.any? && raise(ArgumentError, '`function` does not accept arguments')
267
+ block || raise(ArgumentError, 'block required')
268
+ proc do |*a|
269
+ a.map! {|x| Native(`x`)}
270
+ @this = Native(`this`)
271
+ %x{
272
+ var bs = block.$$s,
273
+ result;
274
+ block.$$s = null;
275
+ result = block.apply(self, a);
276
+ block.$$s = bs;
277
+
278
+ return result;
279
+ }
280
+ end
281
+ end
282
+ alias_method :function, :wedge_function
283
+
284
+ def wedge_javascript
285
+ return unless server?
286
+
287
+ compiled_opts = Base64.encode64 client_wedge_opts.to_json
288
+ name = wedge_opts.file_path.gsub("#{Dir.pwd}/", '').gsub(/\.rb$/, '')
289
+
290
+ javascript = <<-JS
291
+ Wedge.javascript('#{name}', JSON.parse(Base64.decode64('#{compiled_opts}')))
292
+ JS
293
+ "<script>#{Opal.compile(javascript)}</script>"
294
+ end
295
+ alias_method :javscript, :wedge_javascript
296
+
297
+ def client_wedge_opts
298
+ wedge_config.opts_dup.reject {|k, v| REJECTED_CLIENT_OPTS.include? k }
299
+ end
300
+ alias_method :client_opts, :client_wedge_opts
301
+
302
+ def wedge_trigger(*args)
303
+ wedge_opts.events.trigger(*args)
304
+ end
305
+ alias_method :trigger, :wedge_trigger
306
+
307
+ if RUBY_ENGINE == 'opal'
308
+ def wedge(*args)
309
+ Wedge[*args]
310
+ end
311
+ end
312
+
313
+ def method_missing(method, *args, &block)
314
+ if wedge_opts.scope.respond_to?(method, true)
315
+ wedge_opts.scope.send method, *args, &block
316
+ else
317
+ super
318
+ end
319
+ end
320
+ end
321
+ end
@@ -0,0 +1,128 @@
1
+ require 'ostruct'
2
+ require 'wedge/events'
3
+
4
+ module Wedge
5
+ class Config
6
+ include Methods
7
+
8
+ # Stores the options for the config
9
+ #
10
+ # @return [OpenStruct]
11
+ attr_accessor :opts
12
+
13
+ # Setup initial opts values
14
+ #
15
+ # @param opts [Hash] The initial params for #opts.
16
+ def initialize(opts = {})
17
+ opts = {
18
+ cache_assets: false,
19
+ assets_key: false,
20
+ tmpl: IndifferentHash.new,
21
+ scope: false,
22
+ loaded: false,
23
+ requires: [],
24
+ on: [],
25
+ on_server_methods: [],
26
+ object_events: {},
27
+ is_plugin: false,
28
+ assets_url: '/assets/wedge',
29
+ plugins: []
30
+ }.merge opts
31
+
32
+ @opts = OpenStruct.new(opts)
33
+ end
34
+
35
+ # Set the unique name of the component
36
+ #
37
+ # @param name [<String, Symbol>, #to_sym]
38
+ def name(*names)
39
+ names.each do |name|
40
+ opts.name = name.to_sym
41
+ opts.is_plugin = true if name.to_s =~ /_plugin$/
42
+ Wedge.components ||= {}
43
+ Wedge.components[opts.name] = opts
44
+ end
45
+ end
46
+
47
+ def is_plugin?
48
+ opts.is_plugin
49
+ end
50
+
51
+ %w(scope assets_url cache_assets assets_key).each do |m|
52
+ define_method m do |v|
53
+ opts[m] = v
54
+ end
55
+ end
56
+
57
+ # Used to set and update the dom
58
+ def dom
59
+ if server?
60
+ yield
61
+ end
62
+ end
63
+
64
+ # Set the raw html
65
+ # @param html [String]
66
+ def html(html)
67
+ unless RUBY_ENGINE == 'opal'
68
+ opts.html = begin
69
+ File.read html
70
+ rescue
71
+ html
72
+ end.strip
73
+ end
74
+ end
75
+
76
+ def requires(*args)
77
+ unless RUBY_ENGINE == 'opal'
78
+ args.each do |a|
79
+ if a.to_s[/_plugin$/]
80
+ require "wedge/plugins/#{a.to_s.gsub(/_plugin$/, '')}"
81
+ end
82
+ opts.requires << a
83
+ end
84
+ end
85
+ end
86
+
87
+ def opts_dup
88
+ opts.to_h.inject({}) {|copy, (key, value)| copy[key] = value.dup rescue value; copy}
89
+ end
90
+
91
+ def plugin(name)
92
+ unless RUBY_ENGINE == 'opal'
93
+ require "wedge/plugins/#{name}"
94
+ klass = Wedge.components[:"#{name}_plugin"].klass
95
+ Wedge::Component.include(klass::InstanceMethods) if defined?(klass::InstanceMethods)
96
+ Wedge::Component.extend(klass::ClassMethods) if defined?(klass::ClassMethods)
97
+ end
98
+ end
99
+
100
+ def get_requires(requires = false, previous_requires = [])
101
+ list = []
102
+
103
+ unless requires
104
+ requires ||= opts.requires.dup
105
+ previous_requires << opts.name.to_sym
106
+ end
107
+
108
+ previous_requires.each { |p| requires.delete(p) }
109
+
110
+ requires.each do |r|
111
+ klass = Wedge.components[r.to_sym].klass
112
+ o = klass.client_wedge_opts.select do |k, v|
113
+ %w(path_name name requires).include? k.to_s
114
+ end
115
+
116
+ # We don't want to get a stack limit error so we stop something
117
+ # requiring itself
118
+ pr = previous_requires.dup << o[:name].to_sym
119
+
120
+ o[:requires] = get_requires o[:requires].dup, pr if o[:requires].present?
121
+
122
+ list << o
123
+ end
124
+
125
+ list
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,139 @@
1
+ module Wedge
2
+ class DOM
3
+ include Methods
4
+
5
+ attr_accessor :dom, :raw_html
6
+
7
+ class << self
8
+ # Shortcut for creating dom
9
+ # @param html [String]
10
+ # @return dom [DOM]
11
+ def [] html
12
+ new html
13
+ end
14
+ end
15
+
16
+ def initialize html
17
+ @raw_html = html
18
+
19
+ if server?
20
+ @dom = raw_html.is_a?(String) ? HTML[raw_html.dup] : raw_html
21
+ else
22
+ @dom = raw_html.is_a?(String) ? Element[raw_html.dup] : raw_html
23
+ end
24
+ end
25
+
26
+ def find string, &block
27
+ if client?
28
+ node = DOM.new dom.find(string)
29
+ elsif server?
30
+ if block_given?
31
+ node = DOM.new dom.css(string)
32
+ else
33
+ node = DOM.new dom.at(string)
34
+ end
35
+ end
36
+
37
+ if block_given?
38
+ node.each_with_index do |n, i|
39
+ block.call DOM.new(n), i
40
+ end
41
+ end
42
+
43
+ node
44
+ end
45
+
46
+ unless RUBY_ENGINE == 'opal'
47
+ def data key = false, value = false
48
+ d = Hash[node.xpath("@*[starts-with(name(), 'data-')]").map{|a| [a.name, a.value]}]
49
+
50
+ if !key
51
+ d
52
+ elsif key && !value
53
+ d[key]
54
+ else
55
+ node["data-#{key}"] = value
56
+ end
57
+ end
58
+
59
+ def val value
60
+ node.content = value
61
+ end
62
+
63
+ def add_class classes
64
+ classes = (classes || '').split ' ' unless classes.is_a? Array
65
+ new_classes = ((node.attr('class') || '').split(' ') << classes).uniq.join(' ')
66
+ node['class'] = new_classes
67
+ end
68
+
69
+ def remove_class classes
70
+ classes = (classes || '').split ' ' unless classes.is_a? Array
71
+ (node.attr('class') || '').split(' ').reject { |n| n =~ /active|asc|desc/i }.join(' ')
72
+ end
73
+
74
+ def attr key, value = false
75
+ if value
76
+ value = value.join ' ' if value.is_a? Array
77
+ node[key] = value
78
+ else
79
+ super key
80
+ end
81
+ end
82
+ end
83
+
84
+ def html= content
85
+ if server?
86
+ node.inner_html = content
87
+ else
88
+ content = content.dom if content.is_a? Wedge::DOM
89
+ node.html content
90
+ end
91
+
92
+ node
93
+ end
94
+
95
+ if RUBY_ENGINE == 'opal'
96
+ # make it supply the jquery element so it will use that as it doesn't
97
+ # know how to handle the DOM element.
98
+ %w(append prepend replace_with after before).each do |meth|
99
+ define_method meth do |obj|
100
+ obj = obj.dom if obj.is_a? Wedge::DOM
101
+ super obj
102
+ end
103
+ end
104
+
105
+ def to_html
106
+ @dom ||= DOM.new '<div>'
107
+ el = dom.first
108
+ DOM.new('<div>').append(el).html
109
+ end
110
+ end
111
+
112
+ def html content = false
113
+ if !content
114
+ if server?
115
+ node.inner_html
116
+ else
117
+ node ? node.html : dom.html
118
+ end
119
+ else
120
+ self.html = content
121
+ end
122
+ end
123
+
124
+ def node
125
+ @node || dom
126
+ end
127
+
128
+ # This allows you to use all the nokogiri or opal jquery methods if a
129
+ # global one isn't set
130
+ def method_missing method, *args, &block
131
+ # respond_to?(symbol, include_all=false)
132
+ if dom.respond_to? method, true
133
+ dom.send method, *args, &block
134
+ else
135
+ super
136
+ end
137
+ end
138
+ end
139
+ end