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