wedge 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,136 @@
1
+ module Wedge
2
+ class Events
3
+ attr_accessor :scope, :browser_events, :object_events
4
+
5
+ VIP_ORDER_LIST = %w(history_change)
6
+
7
+ def initialize
8
+ @browser_events = []
9
+ @object_events = {}
10
+ end
11
+
12
+ def add(*args, &block)
13
+ event = {
14
+ name: args.shift,
15
+ block: block,
16
+ options: {}
17
+ }
18
+
19
+ args.each do |arg|
20
+ if arg.is_a?(String)
21
+ event[:selector] = arg
22
+ elsif arg.is_a? Class
23
+ event[:klass] = arg
24
+ else
25
+ event[:options] = event[:options].merge(arg).indifferent.to_h
26
+ end
27
+ end
28
+
29
+ if event[:name].to_s == 'ready' || event[:name] =~ /[:\s]/ || event[:selector]
30
+ browser_events << event
31
+ else
32
+ event[:component] = scope.wedge_opts.name
33
+
34
+ if !scope.class.wedge_opts.added_class_events && for_component = event[:options].delete(:for)
35
+ wedge_opts = Wedge.components[for_component].klass.wedge_opts
36
+ events = wedge_opts.object_events[event.delete(:name)] ||= []
37
+ events << event
38
+ else
39
+ events = object_events[event.delete(:name)] ||= []
40
+ events << event
41
+ end
42
+ end
43
+ end
44
+
45
+ def trigger(name, options = {})
46
+ name = name.to_s
47
+ options = options.indifferent
48
+
49
+ case name
50
+ when 'browser_events'
51
+ # We make sure anything in the VIP_ORDER_LIST goes first
52
+ (browser_events.sort_by do |x|
53
+ [VIP_ORDER_LIST.index(x[:name]) || VIP_ORDER_LIST.length, browser_events.index(x)]
54
+ end).each do |event|
55
+ trigger_browser_event event
56
+ end
57
+ else
58
+ events = object_events[name] || []
59
+ events.each do |event|
60
+ Wedge[event[:component]].instance_exec options, &event[:block]
61
+ end
62
+ end
63
+ end
64
+
65
+ def trigger_browser_event event
66
+ comp = Wedge[scope.wedge_opts.name]
67
+
68
+ case event[:name]
69
+ when 'ready'
70
+ el = Element.find(event[:selector] != '' ? event[:selector] : 'body')
71
+
72
+ comp.instance_exec el, &event[:block]
73
+ when 'history_change'
74
+ $window.history.change do |he|
75
+ comp.instance_exec he, &event[:block]
76
+ end
77
+ when 'form'
78
+ warn 'missing form class option' unless event[:klass]
79
+
80
+ Document.on :submit, event[:selector] do |evt|
81
+ el = evt.current_target
82
+ evt.prevent_default
83
+
84
+ params = {}
85
+
86
+ # loop through all the forum values
87
+ el.serialize_array.each do |row|
88
+ field, _ = row
89
+
90
+ # we need to make it native to access it like ruby
91
+ field = Native(field)
92
+ name = field['name']
93
+ value = field['value']
94
+
95
+ params[name] = value
96
+ end
97
+
98
+ params_obj = {}
99
+
100
+ params.each do |param, value|
101
+ keys = param.gsub(/[^a-z0-9_]/, '|').gsub(/\|\|/, '|').gsub(/\|$/, '').split('|')
102
+ params_obj = params_obj.deep_merge keys.reverse.inject(value) { |a, n| { n => a } }
103
+ end
104
+
105
+ opts[:dom] = el
106
+
107
+ if opts && key = opts[:key]
108
+ form = event[:klass].new params_obj[key], opts
109
+ else
110
+ form = event[:klass].new params_obj, opts
111
+ end
112
+
113
+ el.find(opts[:error_selector] || '.field-error').remove
114
+
115
+ comp.instance_exec form, evt.current_target, evt, &event[:block]
116
+ end
117
+ else
118
+ args = [event[:name]]
119
+
120
+ if selector = event[:selector]
121
+ args << selector
122
+ end
123
+
124
+ Document.on(*args) do |evt|
125
+ el = evt.current_target
126
+ comp.instance_exec el, evt, &event[:block]
127
+ end
128
+
129
+ if event[:name] =~ /ready/
130
+ el = Element.find(event[:selector] != '' ? event[:selector] : 'body')
131
+ comp.instance_exec el, &event[:block]
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
data/lib/wedge/html.rb ADDED
@@ -0,0 +1,29 @@
1
+ module Wedge
2
+ module HTML
3
+ include Methods
4
+
5
+ class << self
6
+ # Parse HTML into a Nokogiri object
7
+ # @param raw_html [String]
8
+ # @return parsed_html [Nokogiri]
9
+ def [](raw_html)
10
+ return unless server?
11
+
12
+ # remove all the starting and trailing whitespace
13
+ raw_html = raw_html.strip
14
+
15
+ if raw_html[/\A<!DOCTYPE/] || raw_html[/\A<html/]
16
+ Nokogiri::HTML(raw_html)
17
+ else
18
+ parsed_html = Nokogiri::HTML.fragment(raw_html)
19
+
20
+ if parsed_html.children.length >= 1
21
+ parsed_html.children.first
22
+ else
23
+ parsed_html
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
data/lib/wedge/opal.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'opal'
2
+ require 'opal-jquery'
3
+
4
+ unless RUBY_ENGINE == 'opal'
5
+ module Opal
6
+ class Builder
7
+ # @return [String] Compiled javascript.
8
+ def javascript
9
+ to_s
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ module Wedge
16
+ # Create our own opal instance.
17
+ Opal = ::Opal.dup
18
+ end
@@ -0,0 +1,431 @@
1
+ require 'wedge/plugins/validations'
2
+ require 'forwardable'
3
+
4
+ module Wedge
5
+ module Plugins
6
+ class Form < Component
7
+ config.name :form_plugin
8
+
9
+ include Methods
10
+ include Validations
11
+
12
+ module Delegates
13
+ def _delegates(*names)
14
+ accessors = Module.new do
15
+ extend Forwardable # DISCUSS: do we really need Forwardable here?
16
+ names.each do |name|
17
+ delegate [name, "#{name}="] => :_attributes
18
+ end
19
+ end
20
+ include accessors
21
+ end
22
+ end
23
+
24
+ extend Delegates
25
+
26
+ class Attributes
27
+ def set_values(atts)
28
+ @_attributes = []
29
+
30
+ atts.each do |key, val|
31
+ if respond_to?("#{key}=")
32
+ send(:"#{key}=", val)
33
+ @_attributes << key
34
+ end
35
+ end
36
+ end
37
+
38
+ def set_attr_accessors attrs
39
+ attrs.each do |attr|
40
+ define_singleton_method "#{attr}=" do |value|
41
+ value = value.to_obj if value.is_a? Hash
42
+ instance_variable_set(:"@#{attr}", value)
43
+ @_attributes ||= []
44
+ @_attributes << attr
45
+ end
46
+
47
+ define_singleton_method attr do
48
+ instance_variable_get(:"@#{attr}")
49
+ end
50
+ end
51
+ end
52
+
53
+ def _attributes
54
+ @_attributes ||= []
55
+ end
56
+
57
+ def empty?
58
+ _attributes.empty?
59
+ end
60
+ end
61
+
62
+ # Initialize with a hash of attributes and values.
63
+ # If extra attributes are sent, a NoMethodError exception will be raised.
64
+ #
65
+ # @example
66
+ #
67
+ # class EditPost < Scrivener
68
+ # attr_accessor :title
69
+ # attr_accessor :body
70
+ #
71
+ # def validate
72
+ # assert_present :title
73
+ # assert_present :body
74
+ # end
75
+ # end
76
+ #
77
+ # edit = EditPost.new(title: "Software Tools")
78
+ #
79
+ # edit.valid? #=> false
80
+ #
81
+ # edit.errors[:title] #=> []
82
+ # edit.errors[:body] #=> [:not_present]
83
+ #
84
+ # edit.body = "Recommended reading..."
85
+ #
86
+ # edit.valid? #=> true
87
+ #
88
+ # # Now it's safe to initialize the model.
89
+ # post = Post.new(edit.attributes)
90
+ # post.save
91
+ def initialize(atts = {}, options = {})
92
+ @_data = atts
93
+ @_data = atts.to_obj if atts.is_a? Hash
94
+ @_options = options
95
+
96
+ # @_attributes = Class.new(Attributes).new
97
+ @_attributes = Attributes.new
98
+ @_attributes.set_attr_accessors _attr_accessors
99
+ @_attributes.set_values _data
100
+
101
+ _data.each do |key, val|
102
+ send("#{key}=", val)
103
+ end
104
+
105
+ _form.each do |key, klass|
106
+ opts = {}
107
+ opts[key] = klass.new(_data.send(key)) if _data.respond_to?(key)
108
+ @_attributes.set_values opts
109
+
110
+ send("#{key}=", opts[key])
111
+ end
112
+ end
113
+
114
+ def self.attr_accessor(*vars)
115
+ @_attr_accessors ||= []
116
+ @_form ||= {}
117
+
118
+ vars.each do |v|
119
+ if !v.is_a? Hash
120
+ @_attr_accessors << v unless @_attr_accessors.include? v
121
+ else
122
+ v = v.first
123
+
124
+ unless @_attr_accessors.include? v.first
125
+ @_attr_accessors << v.first
126
+ @_form[v.first] = v.last
127
+ end
128
+ end
129
+ end
130
+
131
+ _delegates(*_attr_accessors)
132
+ end
133
+
134
+ def method_missing method, *args, &block
135
+ # respond_to?(symbol, include_all=false)
136
+ if _data.respond_to? method, true
137
+ _data.send method, *args, &block
138
+ else
139
+ return if method[/\=\z/]
140
+
141
+ super
142
+ end
143
+ end
144
+
145
+ # Return hash of attributes and values.
146
+ def attributes
147
+ Hash.new.tap do |atts|
148
+ _attributes.instance_variables.each do |ivar|
149
+ # todo: figure out why it's setting @constructor and @toString
150
+ next if ivar == :@constructor || ivar == :@toString || ivar == :@_attributes || ivar == :@_data || ivar == :@_forms
151
+
152
+ att = ivar[1..-1].to_sym
153
+ atts[att] = _attributes.send(att)
154
+
155
+ if klass = _form[att.to_s.to_sym]
156
+ atts[att] = klass.new(atts[att]).attributes
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ def model_attributes data = attributes
163
+ hash = {}
164
+
165
+ data.each do |k, v|
166
+ if klass = _form[k.to_s.to_sym]
167
+ d = data[k]
168
+ d = d.attributes if d.is_a?(Form)
169
+
170
+ f = klass.new d
171
+ k = "#{k}_attributes"
172
+ dt = f.model_attributes
173
+
174
+ hash[k] = model_attributes dt
175
+ elsif v.is_a? Hash
176
+ hash[k] = model_attributes data[k]
177
+ else
178
+ hash[k] = v
179
+ end
180
+ end
181
+
182
+ hash
183
+ end
184
+
185
+ def slice(*keys)
186
+ Hash.new.tap do |atts|
187
+ keys.each do |att|
188
+ atts[att] = send(att)
189
+ # atts[att] = _attributes.send(att)
190
+ end
191
+ end
192
+ end
193
+
194
+ def display_errors options = {}, &block
195
+ dom = options.delete(:dom) || _dom
196
+ d_errors = errors
197
+
198
+ if override_errors = options[:override_errors]
199
+ d_errors = override_errors
200
+ end
201
+
202
+ keys = options.delete(:keys) || (_options[:key] ? [_options[:key]] : [])
203
+
204
+ if extra_errors = options.delete(:errors)
205
+ extra_errors.each do |key, value|
206
+ d_errors[key] = value
207
+ end
208
+ end
209
+
210
+ d_errors.each do |key, error|
211
+ d_keys = (keys.dup << key)
212
+
213
+ error = error.first
214
+
215
+ if error.is_a?(Hash)
216
+ d_options = options.dup
217
+ d_options[:keys] = d_keys
218
+ d_options[:override_errors] = d_errors[key].first
219
+
220
+ display_errors d_options, &block
221
+ elsif !block_given? || block.call(d_keys, error) == false
222
+ name = d_keys.each_with_index.map do |field, i|
223
+ i != 0 ? "[#{field}]" : field
224
+ end.join
225
+
226
+ if tmpl = options[:tmpl]
227
+ if client?
228
+ field_error_dom = DOM.new(`#{tmpl.dom}[0].outerHTML`)
229
+ else
230
+ field_error_dom = DOM.new(tmpl.dom.to_html)
231
+ end
232
+ else
233
+ field_error_dom = DOM.new('<span class="field-error"><span>')
234
+ end
235
+
236
+ field_error_dom.html _error_name(key, error)
237
+
238
+ field = dom.find("[name='#{name}']")
239
+ field.before field_error_dom.dom
240
+ end
241
+ end
242
+ end
243
+ alias_method :render_errors, :display_errors
244
+
245
+ def render_values dom = false, key = false, data = false
246
+ dom = _options[:dom] unless dom
247
+ key = _options[:key] if !key && _options.key?(:key)
248
+
249
+ dom.find('input, select, textarea') do |element|
250
+ name = element['name']
251
+ name = name.gsub(/\A#{key}/, '') if key
252
+ keys = name.gsub(/\A\[/, '').gsub(/[^a-z0-9_]/, '|').gsub(/\|\|/, '|').gsub(/\|$/, '').split('|')
253
+ value = false
254
+
255
+ keys.each do |k|
256
+ begin
257
+ value = value ? value.send(k) : send(k)
258
+ rescue
259
+ value = ''
260
+ end
261
+ end
262
+
263
+ case element.name
264
+ when 'select'
265
+ element.find('option') do |x|
266
+ x['selected'] = true if x['value'] == value.to_s
267
+ end
268
+ when 'input'
269
+ if %w(radio checkbox).include? element['type']
270
+ if element['value'] == value.to_s
271
+ element['checked'] = true
272
+ else
273
+ element.delete 'checked'
274
+ end
275
+ else
276
+ value = sprintf('%.2f', value) if value.is_a? BigDecimal
277
+ element['value'] = value.to_s
278
+ end
279
+ when 'textarea'
280
+ element.val value.to_s
281
+ end
282
+ end
283
+ end
284
+
285
+ def _attributes
286
+ @_attributes ||= {}
287
+ end
288
+
289
+ def validate_msg error, column
290
+ false
291
+ end
292
+
293
+ protected
294
+
295
+ def _data
296
+ @_data ||= {}
297
+ end
298
+
299
+ def self._attr_accessors
300
+ @_attr_accessors ||= []
301
+ end
302
+
303
+ def self._form
304
+ @_form || {}
305
+ end
306
+
307
+ def _form
308
+ self.class._form
309
+ end
310
+
311
+ def _attr_accessors
312
+ self.class._attr_accessors
313
+ end
314
+
315
+ def _options
316
+ @_options
317
+ end
318
+
319
+ def _dom
320
+ @_dom ||= @_options[:dom]
321
+ end
322
+
323
+ def _error_name key, error
324
+ validate_msg(error.to_sym, key.to_sym) || case error.to_s.to_sym
325
+ when :not_email
326
+ 'Email Isn\'t Valid.'
327
+ when :not_present
328
+ 'Required.'
329
+ when :not_equal
330
+ 'Password does not match.'
331
+ else
332
+ !error[/\s/] ? error.to_s.gsub(/_/, ' ').titleize : error
333
+ end
334
+ end
335
+
336
+ def empty?
337
+ _attributes.empty?
338
+ end
339
+
340
+ module InstanceMethods
341
+ def render_fields data, options = {}
342
+ data = data.is_a?(Hash) ? data.to_obj : data
343
+
344
+ l_dom = options[:dom] || dom
345
+
346
+ l_dom.find("[data-if]") do |field_dom|
347
+ value = get_value_for field_dom['data-if'], data
348
+
349
+ unless value.present?
350
+ field_dom.remove
351
+ end
352
+ end
353
+
354
+ l_dom.find("[data-unless]") do |field_dom|
355
+ value = get_value_for field_dom['data-unless'], data
356
+
357
+ if value.present?
358
+ field_dom.remove
359
+ end
360
+ end
361
+
362
+ l_dom.find("[data-field]") do |field_dom|
363
+ if field = field_dom['data-field']
364
+ value = get_value_for field, data
365
+
366
+ if !value.nil?
367
+ value = value.to_s
368
+
369
+ if value != value.upcase && !value.match(Wedge::Plugins::Form::EMAIL)
370
+ field_value = value.titleize
371
+ else
372
+ field_value = value
373
+ end
374
+
375
+ field_value = 'No' if field_value == 'False'
376
+ field_value = 'Yes' if field_value == 'True'
377
+
378
+ field_dom.html = field_value
379
+ else
380
+ field_dom.html = ''
381
+ end
382
+ end
383
+ end
384
+
385
+ l_dom
386
+ end
387
+
388
+ def get_value_for field, data
389
+ field = (field || '').split '.'
390
+
391
+ if field.length > 1
392
+ value = data.is_a?(Hash) ? data.to_obj : data
393
+
394
+ field.each_with_index do |f, i|
395
+ # might not have the parent object
396
+ if (value.respond_to?('empty?') ? value.empty? : !value.present?)
397
+ value = ''
398
+ next
399
+ end
400
+
401
+ if (i+1) < field.length
402
+ begin
403
+ value = value.send(f)
404
+ rescue
405
+ value = nil
406
+ end
407
+ else
408
+ begin
409
+ value = value.respond_to?(:present) ? value.present("print_#{f}") : value.send(f)
410
+ rescue
411
+ value = nil
412
+ end
413
+ end
414
+
415
+ end
416
+ else
417
+ begin
418
+ value = data.respond_to?(:present) ? data.present("print_#{field.first}") : data.send(field.first)
419
+ rescue
420
+ value = nil
421
+ end
422
+ end
423
+
424
+ value
425
+ end
426
+ end
427
+ end
428
+ end
429
+ end
430
+
431
+ Wedge::Form = Wedge::Plugins::Form