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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +49 -0
- data/Rakefile +9 -0
- data/lib/roda/plugins/wedge.rb +93 -0
- data/lib/wedge.rb +238 -0
- data/lib/wedge/component.rb +321 -0
- data/lib/wedge/config.rb +128 -0
- data/lib/wedge/dom.rb +139 -0
- data/lib/wedge/events.rb +136 -0
- data/lib/wedge/html.rb +29 -0
- data/lib/wedge/opal.rb +18 -0
- data/lib/wedge/plugins/form.rb +431 -0
- data/lib/wedge/plugins/history.rb +92 -0
- data/lib/wedge/plugins/location.rb +78 -0
- data/lib/wedge/plugins/pjax.rb +65 -0
- data/lib/wedge/plugins/validations.rb +251 -0
- data/lib/wedge/utilis/blank.rb +133 -0
- data/lib/wedge/utilis/element.rb +23 -0
- data/lib/wedge/utilis/hash.rb +77 -0
- data/lib/wedge/utilis/indifferent_hash.rb +209 -0
- data/lib/wedge/utilis/methods.rb +25 -0
- data/lib/wedge/utilis/nokogiri.rb +44 -0
- data/lib/wedge/utilis/titleize.rb +97 -0
- data/lib/wedge/utilis/try.rb +106 -0
- data/lib/wedge/version.rb +3 -0
- data/test.rb +44 -0
- data/test/dummy/app.rb +34 -0
- data/test/dummy/components/bar.rb +14 -0
- data/test/dummy/components/base.rb +5 -0
- data/test/dummy/components/root.rb +42 -0
- data/test/dummy/config.ru +6 -0
- data/test/dummy/forms/bar.rb +5 -0
- data/test/dummy/forms/foo.rb +6 -0
- data/test/test.js +59 -0
- data/test/test_basic_component.rb +34 -0
- data/test/test_browserio.rb +13 -0
- data/test/test_helper.rb +38 -0
- data/wedge.gemspec +32 -0
- metadata +236 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
module Nokogiri
|
2
|
+
module XML
|
3
|
+
class NodeSet
|
4
|
+
# fix: this is really shity
|
5
|
+
# alias_method :original_to_html, :to_html
|
6
|
+
# def to_html *args
|
7
|
+
# original_to_html(*args).gsub('%7B', "{").gsub('%7D', "}")
|
8
|
+
# end
|
9
|
+
end
|
10
|
+
class Node
|
11
|
+
# fix: this is really shity
|
12
|
+
# alias_method :original_to_html, :to_html
|
13
|
+
# def to_html *args
|
14
|
+
# original_to_html(*args).gsub('%7B', "{").gsub('%7D', "}")
|
15
|
+
# end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def coerce data # :nodoc:
|
20
|
+
if data.class.to_s == 'Wedge::DOM'
|
21
|
+
data = data.dom
|
22
|
+
end
|
23
|
+
|
24
|
+
case data
|
25
|
+
when XML::NodeSet
|
26
|
+
return data
|
27
|
+
when XML::DocumentFragment
|
28
|
+
return data.children
|
29
|
+
when String
|
30
|
+
return fragment(data).children
|
31
|
+
when Document, XML::Attr
|
32
|
+
# unacceptable
|
33
|
+
when XML::Node
|
34
|
+
return data
|
35
|
+
end
|
36
|
+
|
37
|
+
raise ArgumentError, <<-EOERR
|
38
|
+
Requires a Node, NodeSet or String argument, and cannot accept a #{data.class}.
|
39
|
+
(You probably want to select a node from the Document with at() or search(), or create a new Node via Node.new().)
|
40
|
+
EOERR
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Adds String#titleize for creating properly capitalized titles.
|
3
|
+
# It can be called as Titleize.titleize or "a string".titleize.
|
4
|
+
#
|
5
|
+
# titlecase is included as an alias for titleize.
|
6
|
+
#
|
7
|
+
# If loaded in a Rails environment, it modifies Inflector.titleize.
|
8
|
+
module Titleize
|
9
|
+
SMALL_WORDS = %w{a an and as at but by en for if in of on or the to v v. via vs vs.}
|
10
|
+
|
11
|
+
extend self
|
12
|
+
|
13
|
+
# Capitalizes most words to create a nicer looking title string.
|
14
|
+
#
|
15
|
+
# The list of "small words" which are not capped comes from
|
16
|
+
# the New York Times Manual of Style, plus 'vs' and 'v'.
|
17
|
+
#
|
18
|
+
# "notes on a scandal" # => "Notes on a Scandal"
|
19
|
+
# "the good german" # => "The Good German"
|
20
|
+
def titleize(title)
|
21
|
+
title = title.dup
|
22
|
+
title.downcase! unless title[/[[:lower:]]/] # assume all-caps need fixing
|
23
|
+
|
24
|
+
phrases(title).map do |phrase|
|
25
|
+
words = phrase.split
|
26
|
+
words.map do |word|
|
27
|
+
def word.capitalize
|
28
|
+
# like String#capitalize, but it starts with the first letter
|
29
|
+
self.sub(/[[:alpha:]].*/) {|subword| subword.capitalize}
|
30
|
+
end
|
31
|
+
|
32
|
+
case word
|
33
|
+
when /[[:alpha:]]\.[[:alpha:]]/ # words with dots in, like "example.com"
|
34
|
+
word
|
35
|
+
when /[-‑]/ # hyphenated word (regular and non-breaking)
|
36
|
+
word.split(/([-‑])/).map do |part|
|
37
|
+
SMALL_WORDS.include?(part) ? part : part.capitalize
|
38
|
+
end.join
|
39
|
+
when /^[[:alpha:]].*[[:upper:]]/ # non-first letter capitalized already
|
40
|
+
word
|
41
|
+
when /^[[:digit:]]/ # first character is a number
|
42
|
+
word
|
43
|
+
when words.first, words.last
|
44
|
+
word.capitalize
|
45
|
+
when *(SMALL_WORDS + SMALL_WORDS.map {|small| small.capitalize })
|
46
|
+
word.downcase
|
47
|
+
else
|
48
|
+
word.capitalize
|
49
|
+
end
|
50
|
+
end.join(" ")
|
51
|
+
end.join(" ")
|
52
|
+
end
|
53
|
+
|
54
|
+
# Splits a title into an array based on punctuation.
|
55
|
+
#
|
56
|
+
# "simple title" # => ["simple title"]
|
57
|
+
# "more complicated: titling" # => ["more complicated:", "titling"]
|
58
|
+
def phrases(title)
|
59
|
+
phrases = title.scan(/.+?(?:[:.;?!] |$)/).map {|phrase| phrase.strip }
|
60
|
+
|
61
|
+
# rejoin phrases that were split on the '.' from a small word
|
62
|
+
if phrases.size > 1
|
63
|
+
phrases[0..-2].each_with_index do |phrase, index|
|
64
|
+
if SMALL_WORDS.include?(phrase.split.last.downcase)
|
65
|
+
phrases[index] << " " + phrases.slice!(index + 1)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
phrases
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class String
|
75
|
+
# Capitalizes most words to create a nicer looking title string.
|
76
|
+
#
|
77
|
+
# The list of "small words" which are not capped comes from
|
78
|
+
# the New York Times Manual of Style, plus 'vs' and 'v'.
|
79
|
+
#
|
80
|
+
# titleize is also aliased as titlecase.
|
81
|
+
#
|
82
|
+
# "notes on a scandal" # => "Notes on a Scandal"
|
83
|
+
# "the good german" # => "The Good German"
|
84
|
+
def titleize(opts={})
|
85
|
+
# if defined? ActiveSupport
|
86
|
+
# ActiveSupport::Inflector.titleize(self, opts)
|
87
|
+
# else
|
88
|
+
Titleize.titleize(self)
|
89
|
+
# end
|
90
|
+
end
|
91
|
+
alias_method :titlecase, :titleize
|
92
|
+
|
93
|
+
def titleize!
|
94
|
+
replace(titleize)
|
95
|
+
end
|
96
|
+
alias_method :titlecase!, :titleize!
|
97
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
class Object
|
2
|
+
# Invokes the public method whose name goes as first argument just like
|
3
|
+
# +public_send+ does, except that if the receiver does not respond to it the
|
4
|
+
# call returns +nil+ rather than raising an exception.
|
5
|
+
#
|
6
|
+
# This method is defined to be able to write
|
7
|
+
#
|
8
|
+
# @person.try(:name)
|
9
|
+
#
|
10
|
+
# instead of
|
11
|
+
#
|
12
|
+
# @person.name if @person
|
13
|
+
#
|
14
|
+
# +try+ calls can be chained:
|
15
|
+
#
|
16
|
+
# @person.try(:spouse).try(:name)
|
17
|
+
#
|
18
|
+
# instead of
|
19
|
+
#
|
20
|
+
# @person.spouse.name if @person && @person.spouse
|
21
|
+
#
|
22
|
+
# +try+ will also return +nil+ if the receiver does not respond to the method:
|
23
|
+
#
|
24
|
+
# @person.try(:non_existing_method) # => nil
|
25
|
+
#
|
26
|
+
# instead of
|
27
|
+
#
|
28
|
+
# @person.non_existing_method if @person.respond_to?(:non_existing_method) # => nil
|
29
|
+
#
|
30
|
+
# +try+ returns +nil+ when called on +nil+ regardless of whether it responds
|
31
|
+
# to the method:
|
32
|
+
#
|
33
|
+
# nil.try(:to_i) # => nil, rather than 0
|
34
|
+
#
|
35
|
+
# Arguments and blocks are forwarded to the method if invoked:
|
36
|
+
#
|
37
|
+
# @posts.try(:each_slice, 2) do |a, b|
|
38
|
+
# ...
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# The number of arguments in the signature must match. If the object responds
|
42
|
+
# to the method the call is attempted and +ArgumentError+ is still raised
|
43
|
+
# in case of argument mismatch.
|
44
|
+
#
|
45
|
+
# If +try+ is called without arguments it yields the receiver to a given
|
46
|
+
# block unless it is +nil+:
|
47
|
+
#
|
48
|
+
# @person.try do |p|
|
49
|
+
# ...
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# You can also call try with a block without accepting an argument, and the block
|
53
|
+
# will be instance_eval'ed instead:
|
54
|
+
#
|
55
|
+
# @person.try { upcase.truncate(50) }
|
56
|
+
#
|
57
|
+
# Please also note that +try+ is defined on +Object+. Therefore, it won't work
|
58
|
+
# with instances of classes that do not have +Object+ among their ancestors,
|
59
|
+
# like direct subclasses of +BasicObject+. For example, using +try+ with
|
60
|
+
# +SimpleDelegator+ will delegate +try+ to the target instead of calling it on
|
61
|
+
# the delegator itself.
|
62
|
+
def try(*a, &b)
|
63
|
+
try!(*a, &b) if a.empty? || respond_to?(a.first)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Same as #try, but raises a NoMethodError exception if the receiver is
|
67
|
+
# not +nil+ and does not implement the tried method.
|
68
|
+
#
|
69
|
+
# "a".try!(:upcase) # => "A"
|
70
|
+
# nil.try!(:upcase) # => nil
|
71
|
+
# 123.try!(:upcase) # => NoMethodError: undefined method `upcase' for 123:Fixnum
|
72
|
+
def try!(*a, &b)
|
73
|
+
if a.empty? && block_given?
|
74
|
+
if b.arity.zero?
|
75
|
+
instance_eval(&b)
|
76
|
+
else
|
77
|
+
yield self
|
78
|
+
end
|
79
|
+
else
|
80
|
+
public_send(*a, &b)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class NilClass
|
86
|
+
# Calling +try+ on +nil+ always returns +nil+.
|
87
|
+
# It becomes especially helpful when navigating through associations that may return +nil+.
|
88
|
+
#
|
89
|
+
# nil.try(:name) # => nil
|
90
|
+
#
|
91
|
+
# Without +try+
|
92
|
+
# @person && @person.children.any? && @person.children.first.name
|
93
|
+
#
|
94
|
+
# With +try+
|
95
|
+
# @person.try(:children).try(:first).try(:name)
|
96
|
+
def try(*args)
|
97
|
+
nil
|
98
|
+
end
|
99
|
+
|
100
|
+
# Calling +try!+ on +nil+ always returns +nil+.
|
101
|
+
#
|
102
|
+
# nil.try!(:name) # => nil
|
103
|
+
def try!(*args)
|
104
|
+
nil
|
105
|
+
end
|
106
|
+
end
|
data/test.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'awesome_print'
|
2
|
+
|
3
|
+
requires = {
|
4
|
+
form_plugin: [],
|
5
|
+
list: [
|
6
|
+
{
|
7
|
+
name: 'claim',
|
8
|
+
requires: [
|
9
|
+
{name: 'filter_form', requires: [{ name: 'form_plugin', requires: [] }]},
|
10
|
+
{name: 'claim_form', requires: []}
|
11
|
+
],
|
12
|
+
},
|
13
|
+
{
|
14
|
+
name: 'claim_form',
|
15
|
+
requires: [
|
16
|
+
{name: 'form_plugin', requires: []},
|
17
|
+
{name: 'address_form', requires: [{ name: 'form_plugin', requires: [] }]}
|
18
|
+
],
|
19
|
+
}
|
20
|
+
]
|
21
|
+
}
|
22
|
+
|
23
|
+
$loaded_requires = []
|
24
|
+
|
25
|
+
def get_requires reqs, requires_array = []
|
26
|
+
new_reqs = []
|
27
|
+
|
28
|
+
reqs.each do |r|
|
29
|
+
if r[:requires].any?
|
30
|
+
get_requires(r[:requires], requires_array)
|
31
|
+
end
|
32
|
+
|
33
|
+
unless $loaded_requires.include? r[:name]
|
34
|
+
$loaded_requires << r[:name]
|
35
|
+
new_reqs << r[:name]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
requires_array << new_reqs if new_reqs.any?
|
40
|
+
|
41
|
+
requires_array
|
42
|
+
end
|
43
|
+
|
44
|
+
ap get_requires requires[:list]
|
data/test/dummy/app.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'wedge'
|
2
|
+
require 'roda'
|
3
|
+
|
4
|
+
require 'pry'
|
5
|
+
require 'awesome_print'
|
6
|
+
|
7
|
+
ROOT_PATH = File.dirname(__FILE__)
|
8
|
+
|
9
|
+
class DummyApp < Roda
|
10
|
+
plugin :wedge, {
|
11
|
+
scope: self,
|
12
|
+
plugins: [:form]
|
13
|
+
}
|
14
|
+
|
15
|
+
plugin :assets, {
|
16
|
+
group_subdirs: false,
|
17
|
+
path: ROOT_PATH,
|
18
|
+
css_dir: '',
|
19
|
+
js_dir: ''
|
20
|
+
}
|
21
|
+
|
22
|
+
route do |r|
|
23
|
+
r.wedge_assets
|
24
|
+
|
25
|
+
r.root do
|
26
|
+
wedge(:root, :js).display
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
Dir["#{ROOT_PATH}/forms/*.rb"].sort.each { |file| require file }
|
32
|
+
Dir["#{ROOT_PATH}/components/*.rb"].sort.each { |file| require file }
|
33
|
+
|
34
|
+
Wedge.cache # cache files on app load
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require_relative 'base' unless RUBY_ENGINE == 'opal'
|
2
|
+
|
3
|
+
class DummyApp
|
4
|
+
class RootComponent < BaseComponent
|
5
|
+
config.name :root
|
6
|
+
config.html <<-HTML
|
7
|
+
<!DOCTYPE html>
|
8
|
+
<html>
|
9
|
+
<head>
|
10
|
+
<script src="//code.jquery.com/jquery-1.11.2.js"></script>
|
11
|
+
#{Wedge.script_tag}
|
12
|
+
</head>
|
13
|
+
<body>
|
14
|
+
<div id='foo'>bar</div>
|
15
|
+
</body>
|
16
|
+
</html>
|
17
|
+
HTML
|
18
|
+
config.dom do
|
19
|
+
dom.find('body') << assets(:js)
|
20
|
+
end
|
21
|
+
config.requires :base, :bar, :foo_form, :pjax_plugin
|
22
|
+
|
23
|
+
def display
|
24
|
+
if server?
|
25
|
+
dom
|
26
|
+
else
|
27
|
+
el = Element['<div>']
|
28
|
+
el.html 'foo'
|
29
|
+
dom.find('#foo').before el
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
on :ready do
|
34
|
+
puts 'dom ready'
|
35
|
+
end
|
36
|
+
|
37
|
+
on :click, '#foo' do |el|
|
38
|
+
el.after '<div>bar</div>'
|
39
|
+
trigger :clicked_foo
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/test/test.js
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
var page = require('webpage').create();
|
2
|
+
page.settings.localToRemoteUrlAccessEnabled = true;
|
3
|
+
page.settings.resourceTimeout = 1000;
|
4
|
+
// page.content = "<!doctype html>\n<html>\n<head>\new<script type=\"text/javascript\" src=\"https://code.jquery.com/jquery-1.11.2.min.js\"></script>\n</head>\n<body>\n<div id=\"foo\">bar<div>\n</body>\n</html>";
|
5
|
+
var content = '<!doctype html>';
|
6
|
+
content += '<html><head>';
|
7
|
+
content += '<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.2.min.js"></script>';
|
8
|
+
content += '</head><body>';
|
9
|
+
content += '<h1 id="foo">bar</h1>';
|
10
|
+
content += '</body></html>';
|
11
|
+
|
12
|
+
// page.content = "<div id='foo'>bar</div>";
|
13
|
+
|
14
|
+
page.onConsoleMessage = function(msg) {
|
15
|
+
console.log(msg);
|
16
|
+
};
|
17
|
+
|
18
|
+
page.onResourceTimeout = function(a) {
|
19
|
+
phantom.exit(1);
|
20
|
+
};
|
21
|
+
|
22
|
+
page.onError = function(msg, trace) {
|
23
|
+
|
24
|
+
var msgStack = ['ERROR: ' + msg];
|
25
|
+
|
26
|
+
if (trace && trace.length) {
|
27
|
+
msgStack.push('TRACE:');
|
28
|
+
trace.forEach(function(t) {
|
29
|
+
msgStack.push(' -> ' + t.file + ': ' + t.line + (t.function ? ' (in function "' + t.function +'")' : ''));
|
30
|
+
});
|
31
|
+
}
|
32
|
+
|
33
|
+
console.log(msgStack.join('\n'));
|
34
|
+
phantom.exit();
|
35
|
+
};
|
36
|
+
|
37
|
+
phantom.onError = function(msg, trace) {
|
38
|
+
var msgStack = ['PHANTOM ERROR: ' + msg];
|
39
|
+
if (trace && trace.length) {
|
40
|
+
msgStack.push('TRACE:');
|
41
|
+
trace.forEach(function(t) {
|
42
|
+
msgStack.push(' -> ' + (t.file || t.sourceURL) + ': ' + t.line + (t.function ? ' (in function ' + t.function +')' : ''));
|
43
|
+
});
|
44
|
+
}
|
45
|
+
console.log(msgStack.join('\n'));
|
46
|
+
phantom.exit();
|
47
|
+
};
|
48
|
+
|
49
|
+
page.content = content
|
50
|
+
|
51
|
+
page.onLoadFinished = function() {
|
52
|
+
page.evaluate(function() {
|
53
|
+
console.log($('#foo').html());
|
54
|
+
});
|
55
|
+
phantom.exit();
|
56
|
+
};
|
57
|
+
|
58
|
+
// page.includeJs("http://code.jquery.com/jquery-1.11.2.min.js", function(){
|
59
|
+
// });
|