iron-web 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.
@@ -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
+