wedge 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
data/lib/wedge/dom.rb ADDED
@@ -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