iron-web 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,154 @@
1
+
2
+ # = HTML Element Class
3
+ #
4
+ # Used with the Html class to generate html content for a single tag/element. Represents
5
+ # a single element with attributes and optional contents (including other elements). Generally, you
6
+ # won't use this by itself. Check out Html.build() instead.
7
+ #
8
+ # Simple useage:
9
+ # >> Html::Element.new('span','some text', :id => 'title-text').render
10
+ # => '<span id="title-text">some text</span>'
11
+ #
12
+ # Complex usage:
13
+ # span = Html::Element.new('span') # Creates a span element for customization
14
+ # span.id = 'title-text' # Set some attributes
15
+ # span.style = 'color: #f00;'
16
+ # span.html = 'some text' # Adds some content
17
+ # span.render # Converts to html string
18
+ # => '<span id="title-text" style="color: #f00;">some text</span>
19
+ #
20
+ class Html
21
+ class Element
22
+ # Add back in our accessors
23
+ attr_accessor :tag, :attrs
24
+
25
+ # Commonly empty tags
26
+ SINGLETON_SET = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source'].freeze
27
+ # Inline formatting tags
28
+ INLINE_SET = ['a','abbr','b','button','em','i','input','img','label','li','option','span','strong','title','textarea','u'].freeze
29
+ # Attributes that should render even if blank
30
+ BLANK_ATTRS = ['value','alt'].freeze
31
+
32
+ # One-stop shop for building content
33
+ def self.build(*args)
34
+ el = self.new(*args)
35
+ yield el if block_given?
36
+ el.render
37
+ end
38
+
39
+ def initialize(tag, text=nil, attrs={})
40
+ @tag = tag.to_s
41
+ @force_end = !SINGLETON_SET.include?(@tag)
42
+ @skip_newline = INLINE_SET.include?(@tag)
43
+
44
+ if text.is_a?(Hash)
45
+ @attrs = text
46
+ text = nil
47
+ else
48
+ @attrs = attrs || {}
49
+ end
50
+
51
+ if text.is_a?(String)
52
+ html << ((!text.respond_to?(:html_safe?) || !text.html_safe?) ? Html.escape_once(text) : text)
53
+ elsif text.is_a?(Html::Element)
54
+ html << text
55
+ elsif text.is_a?(Html)
56
+ @html = text
57
+ end
58
+
59
+ yield self if block_given?
60
+
61
+ self
62
+ end
63
+
64
+ def force_end!
65
+ @force_end = true
66
+ end
67
+
68
+ def skip_newline!
69
+ @skip_newline = true
70
+ end
71
+
72
+ def html
73
+ @html ||= Html.new
74
+ @html
75
+ end
76
+
77
+ def html=(arg)
78
+ if arg.is_a? String
79
+ @html = Html.new
80
+ @html.text! arg
81
+ elsif arg.is_a?(Html)
82
+ @html = arg
83
+ elsif arg.is_a?(Array)
84
+ @html = Html.new
85
+ arg.each do |el|
86
+ @html << el
87
+ end
88
+ else
89
+ raise 'Invalid input'
90
+ end
91
+ end
92
+
93
+ # Set/get attrs on any method missing calls
94
+ def method_missing(method, *args)
95
+ parts = method.to_s.match(/^([a-z0-9_]+)(=?)$/i)
96
+ if parts
97
+ key = parts[1].to_sym
98
+ if parts[2] && args.length == 1
99
+ # We have an attempt to set a missing field...
100
+ @attrs[key] = args[0]
101
+ return args[0]
102
+ else
103
+ raise "I think you meant <#{@tag}>.html.#{method} instead of <#{@tag}>.#{method}" if block_given?
104
+ return @attrs[key]
105
+ end
106
+ else
107
+ # There really is no method...
108
+ super
109
+ end
110
+ end
111
+
112
+ def to_s
113
+ render
114
+ end
115
+
116
+ def inspect
117
+ render
118
+ end
119
+
120
+ def is_a?(other)
121
+ return other == Html::Element
122
+ end
123
+
124
+ def render(depth = 0, inblock = false)
125
+ # Convert attrs to strings
126
+ attrStr = @attrs.collect do |k,v|
127
+ if v.nil?
128
+ nil
129
+ elsif v.blank? && ! BLANK_ATTRS.include?(k.to_s)
130
+ " #{k}"
131
+ else
132
+ v = Html.escape_once(v) unless v.html_safe?
133
+ " #{k}=\"#{v}\""
134
+ end
135
+ end.compact.join('')
136
+
137
+ # Build start tag
138
+ val = ''
139
+ val += "\n" if !inblock && !@skip_newline
140
+ val += ' ' * depth unless !inblock && @skip_newline
141
+ val += "<#{@tag}#{attrStr}>"
142
+ unless @html.nil?
143
+ val += "\n" unless @skip_newline
144
+ val += @html.render(depth+1, !@skip_newline)
145
+ val += "\n" unless @skip_newline || val.ends_with?("\n")
146
+ val += ' ' * depth unless @skip_newline
147
+ end
148
+ val += "</#{@tag}>" if (@force_end || !@html.blank?)
149
+ val += "\n" unless @skip_newline
150
+ val
151
+ end
152
+
153
+ end
154
+ end
@@ -0,0 +1,60 @@
1
+ # Only add these changes if we're NOT running Rails
2
+ unless ''.respond_to?(:html_safe)
3
+
4
+ # Add Rails-like html safeness awareness to objects
5
+ class Object
6
+
7
+ def html_safe?
8
+ false
9
+ end
10
+
11
+ end
12
+
13
+ # Add Rails-like html safeness awareness to strings
14
+ class String
15
+
16
+ # Return a copy of the string marked as being html safe (ie not requiring further encoding)
17
+ def html_safe
18
+ HtmlSafeString.new(self)
19
+ end
20
+
21
+ end
22
+
23
+ # Simple class that extends strings to do html escapes on incoming concats, only defined
24
+ # if not running under Rails
25
+ class HtmlSafeString < String
26
+
27
+ def initialize(*args)
28
+ @html_safe = true
29
+ super
30
+ end
31
+
32
+ def html_safe?
33
+ @html_safe
34
+ end
35
+
36
+ def html_safe
37
+ self
38
+ end
39
+
40
+ # The magic - escape values that are concatenated onto this string
41
+ # before concatenation.
42
+ def concat(value)
43
+ if !html_safe? || value.html_safe?
44
+ super(value)
45
+ else
46
+ super(Html.escape_once(value))
47
+ end
48
+ end
49
+
50
+ def +(other)
51
+ dup.concat(other)
52
+ end
53
+
54
+ def <<(value)
55
+ concat(value)
56
+ end
57
+
58
+ end
59
+
60
+ end
@@ -0,0 +1,228 @@
1
+ require 'cgi'
2
+
3
+ class Url
4
+
5
+ attr_accessor :scheme, :server, :port, :path, :params, :fragment
6
+
7
+ SECURE_SCHEMES = ['https', 'sftp', 'ssh'].freeze
8
+ SECURE_SCHEME_MAPPING = { 'http' => 'https', 'ftp' => 'sftp' }.freeze
9
+ SCHEME_DEFAULT_PORTS = {
10
+ 'ftp' => 21,
11
+ 'ssh' => 22,
12
+ 'smtp' => 25,
13
+ 'http' => 80,
14
+ 'pop3' => 110,
15
+ 'pop' => 110,
16
+ 'sftp' => 115,
17
+ 'imap' => 143,
18
+ 'https' => 443,
19
+ 'ssl' => 443,
20
+ 'irc' => 531,
21
+ 'imaps' => 993,
22
+ 'pop3s' => 995,
23
+ 'pops' => 995
24
+ }.freeze
25
+
26
+ # Returns a new Url from the given string
27
+ def self.parse(str)
28
+ Url.new(str)
29
+ end
30
+
31
+ # Return default domain for urls, used
32
+ # to convert relative urls to absolute
33
+ def self.default_server
34
+ @default_server
35
+ end
36
+
37
+ def self.default_server=(server)
38
+ @default_server = server
39
+ end
40
+
41
+ def self.default_scheme
42
+ @default_scheme || 'http'
43
+ end
44
+
45
+ def self.default_scheme=(scheme)
46
+ @default_scheme = scheme
47
+ end
48
+
49
+ # Construct a full URL from pieces
50
+ def self.build(base, params = {}, fragment = nil)
51
+ url = base + to_param_string(params)
52
+ url += '#' + fragment unless fragment.blank?
53
+ url
54
+ end
55
+
56
+ # Construct a param string from key/value pairs
57
+ def self.to_param_string(params)
58
+ str = ''
59
+ # Remove blank/nil keys
60
+ params.delete_if {|k,v| v.to_s.blank? || k.to_s.blank?}
61
+ # Convert to param string
62
+ unless params.empty?
63
+ str += '?'
64
+ str += params.collect do |k,v|
65
+ if v.is_a?(Array)
66
+ v.collect do |vs|
67
+ val = vs.respond_to?(:to_param) ? vs.to_param : vs
68
+ val = val.to_s
69
+ CGI::escape(k.to_s) + '=' + CGI::escape(val)
70
+ end
71
+ else
72
+ val = v.respond_to?(:to_param) ? v.to_param : v
73
+ val = val.to_s
74
+ CGI::escape(k.to_s) + '=' + CGI::escape(val)
75
+ end
76
+ end.flatten.join('&')
77
+ end
78
+ str
79
+ end
80
+
81
+ def initialize(str = nil)
82
+ @scheme = @port = @server = nil
83
+ set(str)
84
+ end
85
+
86
+ # Parse and set internals from given url
87
+ def set(url)
88
+ # Decompose into major components
89
+ url = (url || '').strip
90
+ base, params, @fragment = url.extract(/^([^\?#]*)\??([^#]*)#?(.*)$/)
91
+
92
+ # Parse out base
93
+ base ||= ''
94
+ if base.match(/^[a-z\+]*:\/\//)
95
+ @scheme, @server, ignore, @port, @path = base.extract(/^([a-z]*):\/\/([a-z0-9\-_\.]+)(:([0-9]+))?(\/.*)?/i)
96
+ @path ||= ''
97
+ @port = @port.blank? ? nil : @port.to_i
98
+ else
99
+ @path = base
100
+ end
101
+
102
+ # Parse out params
103
+ @params = {}
104
+ params.split('&').each do |p|
105
+ k, v = p.split('=')
106
+ add_param(CGI::unescape(k), CGI::unescape(v)) if k && v
107
+ end
108
+ end
109
+
110
+ # Makee the url
111
+ def to_s
112
+ Url::build(base, @params, @fragment)
113
+ end
114
+
115
+ def to_html
116
+ to_s
117
+ end
118
+
119
+ def inspect
120
+ to_s
121
+ end
122
+
123
+ def empty?
124
+ blank?
125
+ end
126
+
127
+ def blank?
128
+ self.base.blank?
129
+ end
130
+
131
+ # Returns the full start of the url, minus params and fragment
132
+ def base
133
+ val = ''
134
+ unless @server.blank?
135
+ val = (@scheme || Url::default_scheme) + '://' + @server
136
+ val += ':' + @port.to_s unless @port.to_s.blank?
137
+ end
138
+ p = (@path || '')
139
+ p = '/' + p unless p.blank? || p.starts_with?('/')
140
+ val + p
141
+ end
142
+
143
+ def append_path(str, escape = false)
144
+ str = str.to_s
145
+ @path ||= ''
146
+ @path += escape ? CGI::escape(str).gsub('+', '%20') : str
147
+ end
148
+
149
+ def +(str)
150
+ append_path(str)
151
+ self
152
+ end
153
+
154
+ # Override current param val or set if none
155
+ def set_param(k, v)
156
+ @params[k] = v
157
+ end
158
+
159
+ # Add a param value (can be called multiply for the same param key)
160
+ def add_param(k, v)
161
+ oldval = @params[k]
162
+ if oldval
163
+ @params[k] = oldval.is_a?(Array) ? oldval + [v] : [oldval, v]
164
+ else
165
+ @params[k] = v
166
+ end
167
+ end
168
+
169
+ # Wipe out keyed value by string match or regex match
170
+ def remove_param(key_or_regex)
171
+ @params.delete_if do |k, v|
172
+ if key_or_regex.is_a?(Regexp)
173
+ k.match(key_or_regex)
174
+ else
175
+ k == key_or_regex
176
+ end
177
+ end
178
+ end
179
+
180
+ # Reset params
181
+ def clear_params
182
+ @params = {}
183
+ end
184
+ def reset_params ; clear_params ; end
185
+
186
+ def has_params?
187
+ @params && !@params.empty?
188
+ end
189
+
190
+ def secure?
191
+ SECURE_SCHEMES.include?(scheme)
192
+ end
193
+
194
+ def relative?
195
+ @server.blank?
196
+ end
197
+
198
+ def absolute?
199
+ !relative?
200
+ end
201
+
202
+ # Ensure url contains a server + scheme section, eg converts '/foo' into 'http://example.com/foo'.
203
+ def make_absolute(secure = false)
204
+ unless absolute? && secure? == secure
205
+ raise 'No default server in Url#make_absolute' unless @server || Url.default_server
206
+ @server = Url.default_server unless @server
207
+ unless @scheme
208
+ @scheme = Url.default_scheme
209
+ @port = nil
210
+ end
211
+ if secure
212
+ raise "No secure scheme for scheme #{@scheme} in Url#make_absolute" unless SECURE_SCHEME_MAPPING[@scheme]
213
+ @scheme = SECURE_SCHEME_MAPPING[@scheme]
214
+ end
215
+ end
216
+ to_s
217
+ end
218
+
219
+ def make_relative
220
+ unless relative?
221
+ @server = nil
222
+ @scheme = nil
223
+ @port = nil
224
+ end
225
+ to_s
226
+ end
227
+
228
+ end
@@ -0,0 +1,9 @@
1
+ # Require our library
2
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'iron', 'web'))
3
+
4
+ RSpec.configure do |config|
5
+ #config.add_formatter 'documentation'
6
+ config.color = true
7
+ config.backtrace_clean_patterns = [/rspec/]
8
+ end
9
+