www_app 1.0.0
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 +36 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +84 -0
- data/TODO.md +13 -0
- data/VERSION +1 -0
- data/bin/www_app +46 -0
- data/doc/Design.md +123 -0
- data/doc/Why_this_arch.rb +104 -0
- data/lib/public/jquery-2.1.1.js +4 -0
- data/lib/public/jquery.serialize-object.min.js +8 -0
- data/lib/public/underscore-1.7.0.js +6 -0
- data/lib/public/underscore-min.map +1 -0
- data/lib/public/underscore.string-2.3.0.js +1 -0
- data/lib/public/www_app.js +824 -0
- data/lib/www_app/Clean.rb +169 -0
- data/lib/www_app/dsl.rb +86 -0
- data/lib/www_app/source.rb +53 -0
- data/lib/www_app.rb +1024 -0
- data/specs/as_ruby/0000-new.rb +23 -0
- data/specs/as_ruby/0010-attrs.rb +29 -0
- data/specs/as_ruby/0011-class.rb +39 -0
- data/specs/as_ruby/0011-href.rb +37 -0
- data/specs/as_ruby/0011-id.rb +39 -0
- data/specs/as_ruby/0020-tag.rb +21 -0
- data/specs/as_ruby/0020-tag_content.rb +43 -0
- data/specs/as_ruby/0021-body.rb +16 -0
- data/specs/as_ruby/0021-form.rb +22 -0
- data/specs/as_ruby/0021-link.rb +26 -0
- data/specs/as_ruby/0021-script.rb +44 -0
- data/specs/as_ruby/0030-mustache.rb +113 -0
- data/specs/as_ruby/0040-css.rb +174 -0
- data/specs/as_ruby/0050-on.rb +64 -0
- data/specs/client-side/index.html +90 -0
- data/specs/client-side/index.js +777 -0
- data/specs/lib/config.ru +96 -0
- data/specs/lib/helpers.rb +230 -0
- data/specs/lib/qunit/qunit-1.15.0.css +237 -0
- data/specs/lib/qunit/qunit-1.15.0.js +2495 -0
- data/specs/lib/sample.rb +23 -0
- data/specs/sampe.2.rb +14 -0
- data/specs/sample.3.rb +17 -0
- data/specs/sample.rb +44 -0
- data/www_app.gemspec +38 -0
- metadata +271 -0
@@ -0,0 +1,169 @@
|
|
1
|
+
|
2
|
+
class WWW_App
|
3
|
+
class Clean
|
4
|
+
|
5
|
+
attr_reader :name, :origin, :actual
|
6
|
+
|
7
|
+
def initialize name, val
|
8
|
+
@name = name[" "] ? name : name.inspect
|
9
|
+
@origin = val
|
10
|
+
@actual = val
|
11
|
+
end
|
12
|
+
|
13
|
+
def update val
|
14
|
+
@actual = val
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
# Examples:
|
19
|
+
#
|
20
|
+
# clean_as :upcase, :string, :in, [1,2,3]
|
21
|
+
# clean_as :upcase, :string, :switch, {...}
|
22
|
+
#
|
23
|
+
def clean_as *args
|
24
|
+
begin
|
25
|
+
cleaner = args.shift
|
26
|
+
case
|
27
|
+
when args.first.is_a?(Array)
|
28
|
+
send cleaner, *(args.shift)
|
29
|
+
when args.first.is_a?(Hash)
|
30
|
+
send cleaner, args.shift
|
31
|
+
else
|
32
|
+
send cleaner
|
33
|
+
end
|
34
|
+
end while !args.empty?
|
35
|
+
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
def not_nil
|
40
|
+
if actual.nil?
|
41
|
+
fail "Invalid: #{name} is required."
|
42
|
+
end
|
43
|
+
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def string
|
48
|
+
return self if actual.is_a?(String)
|
49
|
+
fail "Invalid: #{name} must be a String: #{actual.inspect}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def number
|
53
|
+
return self if actual.is_a?(Numeric)
|
54
|
+
fail "Invalid: #{name} must be a Number: #{actual.inspect}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def not_empty_string
|
58
|
+
string
|
59
|
+
update actual.strip
|
60
|
+
if actual.empty?
|
61
|
+
fail "Invalid: #{name} must not be empty."
|
62
|
+
end
|
63
|
+
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
def downcase
|
68
|
+
not_empty_string
|
69
|
+
update actual.downcase
|
70
|
+
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
def upcase
|
75
|
+
not_empty_string
|
76
|
+
update actual.upcase
|
77
|
+
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
def color
|
82
|
+
not_empty_string
|
83
|
+
if !(actual =~ /\A#[A-Z0-9]{3,10}\Z/i)
|
84
|
+
fail "Invalid: color for #{name}: #{origin.inspect}."
|
85
|
+
end
|
86
|
+
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
def max_length max, msg = nil
|
91
|
+
not_nil
|
92
|
+
if actual.length > 200
|
93
|
+
fail(msg || "#{name} can not be more than #{max}")
|
94
|
+
end
|
95
|
+
|
96
|
+
self
|
97
|
+
end
|
98
|
+
|
99
|
+
def match regex, msg = nil
|
100
|
+
string
|
101
|
+
if !(actual =~ regex)
|
102
|
+
fail(msg || "Invalid: #{name} has invalid chars")
|
103
|
+
end
|
104
|
+
|
105
|
+
self
|
106
|
+
end
|
107
|
+
|
108
|
+
VALID_URL_REGEXP = /\A[a-z0-9\_\-\:\/\?\&\(\)\@\.]{1,200}\Z/i
|
109
|
+
def url
|
110
|
+
max = 200
|
111
|
+
not_empty_string
|
112
|
+
max_length max, "#{name} needs to be #{max} or less chars."
|
113
|
+
match VALID_URL_REGEXP
|
114
|
+
|
115
|
+
self
|
116
|
+
end
|
117
|
+
|
118
|
+
def in *raw
|
119
|
+
choices = raw.flatten
|
120
|
+
if !choices.include?(actual)
|
121
|
+
fail "Invalid: #{name} can't be, #{actual.inspect}, but one of: #{choices.join ", "}"
|
122
|
+
end
|
123
|
+
|
124
|
+
self
|
125
|
+
end
|
126
|
+
|
127
|
+
def switch choices
|
128
|
+
self.in(choices.keys)
|
129
|
+
update choices[actual]
|
130
|
+
|
131
|
+
self
|
132
|
+
end
|
133
|
+
|
134
|
+
def map action, *args
|
135
|
+
update(
|
136
|
+
actual.map { |v|
|
137
|
+
Clean.new("#{name} value", v).
|
138
|
+
send(action, *args).
|
139
|
+
actual
|
140
|
+
}
|
141
|
+
)
|
142
|
+
|
143
|
+
self
|
144
|
+
end
|
145
|
+
|
146
|
+
def number_between min, max
|
147
|
+
number
|
148
|
+
if actual < min || actual > max
|
149
|
+
fail "Invalid: #{name}, #{actual.inspect}, must be between: #{min} and #{max}"
|
150
|
+
end
|
151
|
+
|
152
|
+
self
|
153
|
+
end
|
154
|
+
|
155
|
+
# ================= HTML-specific =====================================================
|
156
|
+
|
157
|
+
VALID_FONT_REGEXP = /\A[a-z0-9\-\_\ ]{1,100}\Z/i
|
158
|
+
|
159
|
+
def fonts
|
160
|
+
map :not_empty_string
|
161
|
+
map :match, VALID_FONT_REGEXP, "only allow 1-100 characters: letters, numbers, spaces, - _"
|
162
|
+
|
163
|
+
self
|
164
|
+
end
|
165
|
+
|
166
|
+
# =====================================================================================
|
167
|
+
|
168
|
+
end # === class Clean
|
169
|
+
end # === class WWW_App
|
data/lib/www_app/dsl.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
|
2
|
+
about(:input) {
|
3
|
+
self_close
|
4
|
+
|
5
|
+
input!(:hidden, :Symbol, :Symbol) {
|
6
|
+
attrs :type=>:hidden, :name=>args.second, :value=>args.third)
|
7
|
+
}
|
8
|
+
|
9
|
+
fieldset {
|
10
|
+
input {
|
11
|
+
attrs(:value=> '')
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
fieldset(:Symbol) {
|
16
|
+
input {
|
17
|
+
attrs type: 'text'.freeze, name: args_of(:parent).first
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
fieldset(:password) {
|
22
|
+
input! {
|
23
|
+
attrs :type=>'password'.freeze, :name=>'password'.freeze
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
fieldset(:password, :Symbol) {
|
28
|
+
input {
|
29
|
+
attrs :name => args_of(:parent).second
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
fieldset(:password_confirm) {
|
34
|
+
input! {
|
35
|
+
attrs :type=>'password'.freeze, :name=>'password_confirm'.freeze, :value=>''
|
36
|
+
on(:validate) { should_equal :password }
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
}
|
41
|
+
|
42
|
+
|
43
|
+
# -------------------------------------------------------------------
|
44
|
+
# -------------------------------------------------------------------
|
45
|
+
# -------------------------------------------------------------------
|
46
|
+
# -------------------------------------------------------------------
|
47
|
+
# -------------------------------------------------------------------
|
48
|
+
form.sign_in! {
|
49
|
+
|
50
|
+
title "Sign-in"
|
51
|
+
|
52
|
+
div.main {
|
53
|
+
|
54
|
+
fieldset(:username) do
|
55
|
+
box {
|
56
|
+
label 'Username'
|
57
|
+
tip 'some tip'
|
58
|
+
}
|
59
|
+
|
60
|
+
input {
|
61
|
+
on(:validate){ validate "[0-9a-zA-Z.-_]{0,25}" }
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
fieldset(:password) do
|
66
|
+
title 'Pass phrase:'
|
67
|
+
input
|
68
|
+
end
|
69
|
+
|
70
|
+
fieldset(:password_confirm) do
|
71
|
+
title 'Confirm pass phrase:'
|
72
|
+
input
|
73
|
+
end
|
74
|
+
}
|
75
|
+
|
76
|
+
div.footer {
|
77
|
+
div.buttons do
|
78
|
+
submit 'Sign-in'
|
79
|
+
cancel 'Cancel' do
|
80
|
+
reset
|
81
|
+
hide
|
82
|
+
end
|
83
|
+
end
|
84
|
+
}
|
85
|
+
|
86
|
+
}
|
@@ -0,0 +1,53 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
class WWW_App
|
4
|
+
class Code_ify < BasicObject
|
5
|
+
def initialize &blok
|
6
|
+
@code = []
|
7
|
+
@target = @code
|
8
|
+
@indent = []
|
9
|
+
instance_eval(&blok) if blok
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing *args, &blok
|
13
|
+
@target << {:name=>args.shift, :args=>args}
|
14
|
+
|
15
|
+
if blok
|
16
|
+
@indent << 1
|
17
|
+
@target.last[:blok] = []
|
18
|
+
@target.last[:indent] = @indent.size
|
19
|
+
|
20
|
+
orig = @target
|
21
|
+
@target = @target.last[:blok]
|
22
|
+
instance_eval(&blok)
|
23
|
+
|
24
|
+
@indent.pop
|
25
|
+
@target = orig
|
26
|
+
end
|
27
|
+
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_text
|
32
|
+
@code.map { |e|
|
33
|
+
to_string e
|
34
|
+
}.join "\n".freeze
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def to_string e
|
39
|
+
if e.is_a? ::Array
|
40
|
+
return e.map { |ee| to_string ee }.join "\n".freeze
|
41
|
+
end
|
42
|
+
|
43
|
+
s = %^#{e[:name]}(#{e[:args].map(&:inspect).join ', '.freeze})^
|
44
|
+
if e[:blok]
|
45
|
+
first_indent = ' '.freeze * e[:indent]
|
46
|
+
last_indent = ' '.freeze * (e[:indent] - 1)
|
47
|
+
s << %^ {\n#{first_indent}#{to_string e[:blok]}\n#{last_indent}}^
|
48
|
+
end
|
49
|
+
s
|
50
|
+
end
|
51
|
+
|
52
|
+
end # === class
|
53
|
+
end # === class
|