wedge 0.0.1

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