web-utils 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +242 -0
- data/lib/web_utils.rb +232 -0
- data/test/test_web_utils.rb +524 -0
- data/web-utils.gemspec +20 -0
- metadata +66 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 406c990b91a61d30f76d4ff065013eaf1ec741ea
|
4
|
+
data.tar.gz: 4788cfe256f7e536de8067c79bef7193a35ed0e7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2a675233f99d1b6b7299e830c6d80fbf46637b2d9c43c1a35753887ec1f254b126f4e0cfcb439d080a1c11eb80bd527f0807c4030f352365d44644b23ddea476
|
7
|
+
data.tar.gz: 08f910f903304d445e32a66f20ad8bb9c025d6b5516f4437b83ae8f18654d0633d9c05b40275033b2cd2b451fc636ab41a0496e8ae978e4215fe86d69d18edb4
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2016 Mickael Riga
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
20
|
+
|
data/README.md
ADDED
@@ -0,0 +1,242 @@
|
|
1
|
+
WebUtils
|
2
|
+
========
|
3
|
+
|
4
|
+
`WebUtils` is basically a collection of useful helper
|
5
|
+
methods that are quite common to have in a web project.
|
6
|
+
It is organized like `Rack::Utils` and actually extends it.
|
7
|
+
|
8
|
+
Some of the methods are similar to methods you would have
|
9
|
+
in `Active::Support` but without monkey patching. Although it
|
10
|
+
is only a coincidence. The purpose is not to build an alternative.
|
11
|
+
|
12
|
+
Here is how you would use it with `Sinatra`:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
require 'sinatra/base'
|
16
|
+
require 'web_utils'
|
17
|
+
|
18
|
+
class Main < Sinatra::Base
|
19
|
+
|
20
|
+
# Your frontend code...
|
21
|
+
|
22
|
+
helpers do
|
23
|
+
include WebUtils
|
24
|
+
# Your other helpers...
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
```
|
29
|
+
|
30
|
+
Some methods are also useful on the model side.
|
31
|
+
|
32
|
+
Here is a list of the available methods and what they do:
|
33
|
+
|
34
|
+
`blank?(string)`
|
35
|
+
----------------
|
36
|
+
|
37
|
+
Just tells you if the string is blank or not.
|
38
|
+
|
39
|
+
`pluralize(string)`
|
40
|
+
-------------------
|
41
|
+
|
42
|
+
Pluralize simple words. Just override when necessary.
|
43
|
+
|
44
|
+
`singularize(string)`
|
45
|
+
-------------------
|
46
|
+
|
47
|
+
Singularize simple words. Just override when necessary.
|
48
|
+
|
49
|
+
`dasherize_class_name(string)`
|
50
|
+
------------------------------
|
51
|
+
|
52
|
+
Dasherize class names. Module separator is a double dash.
|
53
|
+
So `BlogArticle::Comment` becomes `blog-article--comment`.
|
54
|
+
Also for simplicity, it does not gather accronyms. e.g. `net--f-t-p`.
|
55
|
+
This is useful for urls or creating CSS class names or IDs.
|
56
|
+
|
57
|
+
`undasherize_class_name(string)`
|
58
|
+
--------------------------------
|
59
|
+
|
60
|
+
Basically the opposite.
|
61
|
+
|
62
|
+
`resolve_class_name(string, context=Kernel)`
|
63
|
+
--------------------------------------------
|
64
|
+
|
65
|
+
It takes the class name as a string and returns the class.
|
66
|
+
You can pass a class name with modules as well (e.g. "Net::FTP").
|
67
|
+
This is actually the main reason why there is a `context`
|
68
|
+
argument, because it uses recursion to do this.
|
69
|
+
But `context` is still useful otherwise.
|
70
|
+
|
71
|
+
`resolve_dasherized_class_name(string)`
|
72
|
+
---------------------------------------
|
73
|
+
|
74
|
+
Same except that it takes the dasherized version of the
|
75
|
+
class name as an argument and returns the class itself (not a string).
|
76
|
+
Useful for resolving a class from a URL param.
|
77
|
+
|
78
|
+
`guess_related_class_name(parent_class, string)`
|
79
|
+
------------------------------------------------
|
80
|
+
|
81
|
+
It is mainly used for guessing the class name of a
|
82
|
+
children class with a plural name.
|
83
|
+
So `guess_related_class_name(BlogArticle, :comments)`
|
84
|
+
will return `'BlogArticle::Comment'`.
|
85
|
+
|
86
|
+
`get_value(value, target=Kernel)`
|
87
|
+
----------------------------------
|
88
|
+
|
89
|
+
It is used for example for getting a default value for something
|
90
|
+
and it is passed either as:
|
91
|
+
|
92
|
+
- A direct value (e.g. `"John Doe"`)
|
93
|
+
- A proc (e.g. `proc{ Time.now }`)
|
94
|
+
- A symbol (e.g. `:get_last_value`)
|
95
|
+
|
96
|
+
In the case of a symbol, the message is called on the target.
|
97
|
+
Therefore you would always give the target just in case it
|
98
|
+
is a symbol.
|
99
|
+
|
100
|
+
If the value can only be direct or a Proc, you can ignore the
|
101
|
+
second argument (target).
|
102
|
+
|
103
|
+
`deep_copy(object)`
|
104
|
+
-------------------
|
105
|
+
|
106
|
+
This makes a deeper copy of an object, since `dup` does not
|
107
|
+
duplicate nested objects in a hash for example. It uses a simple
|
108
|
+
marshal/unmarshal mechanism.
|
109
|
+
|
110
|
+
It is a bit of a hack and not that web-specific, but useful
|
111
|
+
if you want to avoid some nasty bugs.
|
112
|
+
|
113
|
+
`ensure_key!(hash, key, default_value)`
|
114
|
+
---------------------------------------
|
115
|
+
|
116
|
+
If the hash does not have the key, it sets it with the
|
117
|
+
default value. And this value is also returned by the method.
|
118
|
+
|
119
|
+
`ensure_key(hash, key, default_value)`
|
120
|
+
--------------------------------------
|
121
|
+
|
122
|
+
Same as `ensure_key!` except that it does not change the original
|
123
|
+
hash. It returns a new one.
|
124
|
+
|
125
|
+
`slugify(string, force_lowercase=true)`
|
126
|
+
-----------------
|
127
|
+
|
128
|
+
This makes the strings ready to be used as a slug in a URL.
|
129
|
+
It removes the accents, replaces a lot of separators with
|
130
|
+
dashes and escapes it. By default it forces the output to
|
131
|
+
be lowercase, but if you pass `false` as a second argument,
|
132
|
+
it will not change the case of letters.
|
133
|
+
|
134
|
+
`label_for_field(string_or_symbol)`
|
135
|
+
-----------------------------------
|
136
|
+
|
137
|
+
Returns a human readable version of a field name.
|
138
|
+
It says `field`, but it could be any kind of symbol I guess.
|
139
|
+
|
140
|
+
`each_stub(nested_object) {|object,key_or_index,value| ... }`
|
141
|
+
-------------------------------------------------------------
|
142
|
+
|
143
|
+
It is used to run something on all the nested stubs of
|
144
|
+
an array or a hash. The second argument of the block is
|
145
|
+
either a key if the object is a hash, or an index if the
|
146
|
+
object is an array.
|
147
|
+
|
148
|
+
`automatic_typecast(string, casted=[:bool, :nil, :int, :float])`
|
149
|
+
----------------------------
|
150
|
+
|
151
|
+
It tries to change a string value received by an HTML form
|
152
|
+
or a CSV file into an object when it can. So far it recognizes
|
153
|
+
simple things like `true`, `false`, integers and floats.
|
154
|
+
And an empty string is always `nil`.
|
155
|
+
|
156
|
+
The second argument is the list of things you want to typecast.
|
157
|
+
By default there is everything, but you only want to typecast
|
158
|
+
integers and floats, you can pass `[:int, :float]`.
|
159
|
+
|
160
|
+
`generate_random_id(size)`
|
161
|
+
--------------------------
|
162
|
+
|
163
|
+
Like the name suggests, it generates a random string of
|
164
|
+
only letters and numbers. If you don't provide a size,
|
165
|
+
it defaults to 16.
|
166
|
+
|
167
|
+
`nl2br(string, br="<br>")`
|
168
|
+
--------------------------
|
169
|
+
|
170
|
+
The classic `nl2br` which makes sure return lines are
|
171
|
+
turned into `<br>` tags. You can use the second argument if
|
172
|
+
you want to specify what the replacement tag should be.
|
173
|
+
Just in case you want self-closing tags.
|
174
|
+
|
175
|
+
`complete_link(string)`
|
176
|
+
-----------------------
|
177
|
+
|
178
|
+
This just makes sure that a link is complete. Very often
|
179
|
+
people tend to enter a URL like `www.google.com` which is a
|
180
|
+
controversial `href` for some browsers, so it changes it to
|
181
|
+
`//www.google.com`. Already seemingly complete links are not
|
182
|
+
affected by the method.
|
183
|
+
|
184
|
+
`external_link?(string)`
|
185
|
+
------------------------
|
186
|
+
|
187
|
+
This tells you if a link is pointing to the current site or
|
188
|
+
an external one. This is useful when you want to create a link
|
189
|
+
tag and want to decide if target is `'_blank'` or `'_self'`.
|
190
|
+
|
191
|
+
`automatic_html(string, br="<br>")`
|
192
|
+
-----------------------------------
|
193
|
+
|
194
|
+
This automatically does `nl2br` and links recognizable things
|
195
|
+
like email addresses and URLs. Not as good as markdown, but it
|
196
|
+
is quite useful, should it be only for turning an email into a link.
|
197
|
+
|
198
|
+
`truncate(string, size=320, ellipsis="...")`
|
199
|
+
--------------------------------------------
|
200
|
+
|
201
|
+
It truncates a string like what you have in blog summaries.
|
202
|
+
It automatically removes tags and line breaks. The length is
|
203
|
+
320 by default. When the original string was longer, it puts
|
204
|
+
an ellipsis at the end which can be replaced by whatever you put
|
205
|
+
as a 3rd argument. e.g. `'...and more'`.
|
206
|
+
|
207
|
+
`display_price(int)`
|
208
|
+
--------------------
|
209
|
+
|
210
|
+
It changes a price in cents/pence into a formated string
|
211
|
+
like `49,425.40` when you pass `4942540`.
|
212
|
+
|
213
|
+
`parse_price(string)`
|
214
|
+
---------------------
|
215
|
+
|
216
|
+
It does the opposite of `display_price` and parses a string in
|
217
|
+
order to return a price in cents/pence.
|
218
|
+
|
219
|
+
`branded_filename(path, brand="WebUtils")`
|
220
|
+
------------------------------------------
|
221
|
+
|
222
|
+
It takes the path to a file and add the brand/prefix and a dash
|
223
|
+
before the file name (really the file name, not the path).
|
224
|
+
By default, the brand/prefix is `WebUtils`.
|
225
|
+
|
226
|
+
`filename_variation(path, variation, ext)`
|
227
|
+
------------------------------------------
|
228
|
+
|
229
|
+
For example you have a file `/path/to/image.jpg` and you want
|
230
|
+
to create its `thumbnail` in `png`, you can create the thumnail
|
231
|
+
path with `filename_variation(path, :thumbnail, :png)` and it
|
232
|
+
will return `/path/to/image.thumbnail.png`.
|
233
|
+
|
234
|
+
`initial_request?(request)`
|
235
|
+
---------------------------
|
236
|
+
|
237
|
+
You basically pass the `Request` object to the method and it
|
238
|
+
looks at the referrer and returns true if it was not on the same
|
239
|
+
domain. Essentially tells you if the visitor just arrived.
|
240
|
+
|
241
|
+
|
242
|
+
|
data/lib/web_utils.rb
ADDED
@@ -0,0 +1,232 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rack/utils'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
module WebUtils
|
7
|
+
|
8
|
+
# Most methods are supposed to be as simple as possible
|
9
|
+
# and just cover most cases.
|
10
|
+
# I would rather override specific cases rather than making
|
11
|
+
# complicated methods.
|
12
|
+
|
13
|
+
extend Rack::Utils
|
14
|
+
|
15
|
+
def blank? s
|
16
|
+
s.to_s.strip==''
|
17
|
+
end
|
18
|
+
module_function :blank?
|
19
|
+
|
20
|
+
def pluralize s
|
21
|
+
s<<'e' if s[-1,1]=='x'
|
22
|
+
s<<'s'
|
23
|
+
s.sub(/([b-df-hj-np-tv-z])ys$/,'\1ies')
|
24
|
+
end
|
25
|
+
module_function :pluralize
|
26
|
+
|
27
|
+
def singularize s
|
28
|
+
case s
|
29
|
+
when /xes$/
|
30
|
+
s[0..-3]
|
31
|
+
when /ies$/
|
32
|
+
s.sub(/ies$/, 'y')
|
33
|
+
when /s$/
|
34
|
+
s[0..-2]
|
35
|
+
else
|
36
|
+
s
|
37
|
+
end
|
38
|
+
end
|
39
|
+
module_function :singularize
|
40
|
+
|
41
|
+
def dasherize_class_name s
|
42
|
+
s.gsub(/([A-Z]|\d+)/){|str|"-#{str.downcase}"}[1..-1].gsub('::','-')
|
43
|
+
end
|
44
|
+
module_function :dasherize_class_name
|
45
|
+
|
46
|
+
def undasherize_class_name s
|
47
|
+
s.capitalize.gsub(/\-([a-z0-9])/){|str|$1.upcase}.gsub('-','::')
|
48
|
+
end
|
49
|
+
module_function :undasherize_class_name
|
50
|
+
|
51
|
+
def resolve_class_name s, context=Kernel
|
52
|
+
current, *payload = s.to_s.split('::')
|
53
|
+
raise(NameError) if current.nil?
|
54
|
+
const = context.const_get(current)
|
55
|
+
if payload.empty?
|
56
|
+
const
|
57
|
+
else
|
58
|
+
resolve_class_name(payload.join('::'),const)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
module_function :resolve_class_name
|
62
|
+
|
63
|
+
def resolve_dasherized_class_name s
|
64
|
+
resolve_class_name(undasherize_class_name(s.to_s))
|
65
|
+
end
|
66
|
+
module_function :resolve_dasherized_class_name
|
67
|
+
|
68
|
+
def guess_related_class_name context, clue
|
69
|
+
context.respond_to?(:name) ? context.name : context.to_s
|
70
|
+
clue = clue.to_s
|
71
|
+
return clue if clue=~/^[A-Z]/
|
72
|
+
if clue=~/^[a-z]/
|
73
|
+
clue = undasherize_class_name singularize(clue).gsub('_','-')
|
74
|
+
clue = "::#{clue}"
|
75
|
+
end
|
76
|
+
"#{context}#{clue}"
|
77
|
+
end
|
78
|
+
module_function :guess_related_class_name
|
79
|
+
|
80
|
+
def get_value raw, context=Kernel
|
81
|
+
if raw.is_a? Proc
|
82
|
+
raw.call
|
83
|
+
elsif raw.is_a? Symbol
|
84
|
+
context.__send__ raw
|
85
|
+
else
|
86
|
+
raw
|
87
|
+
end
|
88
|
+
end
|
89
|
+
module_function :get_value
|
90
|
+
|
91
|
+
def deep_copy original
|
92
|
+
Marshal.load(Marshal.dump(original))
|
93
|
+
end
|
94
|
+
module_function :deep_copy
|
95
|
+
|
96
|
+
def ensure_key! h, k, v
|
97
|
+
h[k] = v unless h.key?(k)
|
98
|
+
h[k]
|
99
|
+
end
|
100
|
+
module_function :ensure_key!
|
101
|
+
|
102
|
+
def ensure_key h, k, v
|
103
|
+
new_h = h.dup
|
104
|
+
self.ensure_key! new_h, k, v
|
105
|
+
new_h
|
106
|
+
end
|
107
|
+
module_function :ensure_key
|
108
|
+
|
109
|
+
ACCENTS_FROM =
|
110
|
+
"ÀÁÂÃÄÅàáâãäåĀāĂ㥹ÇçĆćĈĉĊċČčÐðĎďĐđÈÉÊËèéêëĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħÌÍÎÏìíîïĨĩĪīĬĭĮįİıĴĵĶķĸĹĺĻļĽľĿŀŁłÑñŃńŅņŇňʼnŊŋÒÓÔÕÖØòóôõöøŌōŎŏŐőŔŕŖŗŘřŚśŜŝŞ"
|
111
|
+
ACCENTS_TO =
|
112
|
+
"AAAAAAaaaaaaAaAaAaCcCcCcCcCcDdDdDdEEEEeeeeEeEeEeEeEeGgGgGgGgHhHhIIIIiiiiIiIiIiIiIiJjKkkLlLlLlLlLlNnNnNnNnnNnOOOOOOooooooOoOoOoRrRrRrSsSsSsSssT"
|
113
|
+
def slugify s, force_lower=true
|
114
|
+
s = s.to_s.tr(ACCENTS_FROM,ACCENTS_TO).tr(' .,;:?!/\'"()[]{}<>','-').gsub(/&/, 'and').gsub(/-+/,'-').gsub(/(^-|-$)/,'')
|
115
|
+
s = s.downcase if force_lower
|
116
|
+
escape(s)
|
117
|
+
end
|
118
|
+
module_function :slugify
|
119
|
+
|
120
|
+
def label_for_field field_name
|
121
|
+
field_name.to_s.scan(/[a-zA-Z0-9]+/).map(&:capitalize).join(' ')
|
122
|
+
end
|
123
|
+
module_function :label_for_field
|
124
|
+
|
125
|
+
def each_stub obj, &block
|
126
|
+
raise TypeError, 'WebUtils.each_stub expects an object which respond to each_with_index' unless obj.respond_to?(:each_with_index)
|
127
|
+
obj.each_with_index do |(k,v),i|
|
128
|
+
value = v || k
|
129
|
+
if value.is_a?(Hash) || value.is_a?(Array)
|
130
|
+
each_stub(value,&block)
|
131
|
+
else
|
132
|
+
block.call(obj, (v.nil? ? i : k), value)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
module_function :each_stub
|
137
|
+
|
138
|
+
def automatic_typecast str, casted=[:bool,:nil,:int,:float]
|
139
|
+
return str unless str.is_a?(String)
|
140
|
+
if casted.include?(:bool) and str=='true'
|
141
|
+
true
|
142
|
+
elsif casted.include?(:bool) and str=='false'
|
143
|
+
false
|
144
|
+
elsif casted.include?(:nil) and str==''
|
145
|
+
nil
|
146
|
+
elsif casted.include?(:int) and str=~/^-?\d+$/
|
147
|
+
str.to_i
|
148
|
+
elsif casted.include?(:float) and str=~/^-?\d*\.\d+$/
|
149
|
+
str.to_f
|
150
|
+
else
|
151
|
+
str
|
152
|
+
end
|
153
|
+
end
|
154
|
+
module_function :automatic_typecast
|
155
|
+
|
156
|
+
ID_CHARS = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a
|
157
|
+
ID_SIZE = 16
|
158
|
+
def generate_random_id size=ID_SIZE
|
159
|
+
id = ''
|
160
|
+
size.times{id << ID_CHARS[rand(ID_CHARS.size)]}
|
161
|
+
id
|
162
|
+
end
|
163
|
+
module_function :generate_random_id
|
164
|
+
|
165
|
+
def nl2br s, br='<br>'
|
166
|
+
s.to_s.gsub(/\n/,br)
|
167
|
+
end
|
168
|
+
module_function :nl2br
|
169
|
+
|
170
|
+
def complete_link link
|
171
|
+
if blank?(link) or link=~/^(\/|[a-z]*:)/
|
172
|
+
link
|
173
|
+
else
|
174
|
+
"//#{link}"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
module_function :complete_link
|
178
|
+
|
179
|
+
def external_link? link
|
180
|
+
!!(link =~ /^[a-z]*:?\/\//)
|
181
|
+
end
|
182
|
+
module_function :external_link?
|
183
|
+
|
184
|
+
def automatic_html s, br='<br>'
|
185
|
+
replaced = s.to_s.
|
186
|
+
gsub(/\b((https?:\/\/|ftps?:\/\/|www\.)([A-Za-z0-9\-_=%&@\?\.\/]+))\b/) do |str|
|
187
|
+
url = complete_link $1
|
188
|
+
"<a href='#{url}' target='_blank'>#{$1}</a>"
|
189
|
+
end.
|
190
|
+
gsub(/([^\s]+@[^\s]*[a-zA-Z])/) do |str|
|
191
|
+
"<a href='mailto:#{$1.downcase}'>#{$1}</a>"
|
192
|
+
end
|
193
|
+
nl2br(replaced,br).gsub("@", "@")
|
194
|
+
end
|
195
|
+
module_function :automatic_html
|
196
|
+
|
197
|
+
def truncate s,c=320,ellipsis='...'
|
198
|
+
s.to_s.gsub(/<[^>]*>/, '').gsub(/\n/, ' ').sub(/^(.{#{c}}\w*).*$/m, '\1'+ellipsis)
|
199
|
+
end
|
200
|
+
module_function :truncate
|
201
|
+
|
202
|
+
def display_price int
|
203
|
+
raise(TypeError, 'The price needs to be the price in cents/pence as an integer') unless int.is_a?(Integer)
|
204
|
+
("%.2f" % (int/100.0)).sub(/\.00/, '').reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
|
205
|
+
end
|
206
|
+
module_function :display_price
|
207
|
+
|
208
|
+
def parse_price string
|
209
|
+
raise(TypeError, 'The price needs to be parsed from a String') unless string.is_a?(String)
|
210
|
+
("%.2f" % string.gsub(/[^\d\.\-]/, '')).gsub(/\./,'').to_i
|
211
|
+
end
|
212
|
+
module_function :parse_price
|
213
|
+
|
214
|
+
def branded_filename path, brand='WebUtils'
|
215
|
+
"#{File.dirname(path)}/#{brand}-#{File.basename(path)}".sub(/^\.\//,'')
|
216
|
+
end
|
217
|
+
module_function :branded_filename
|
218
|
+
|
219
|
+
def filename_variation path, variation, ext
|
220
|
+
old_ext = File.extname(path)
|
221
|
+
path.sub(/#{Regexp.escape old_ext}$/, ".#{variation}.#{ext}")
|
222
|
+
end
|
223
|
+
module_function :filename_variation
|
224
|
+
|
225
|
+
def initial_request? request
|
226
|
+
return true unless request.referer=~URI.regexp
|
227
|
+
URI.parse(request.referer).host!=request.host
|
228
|
+
end
|
229
|
+
module_function :initial_request?
|
230
|
+
|
231
|
+
end
|
232
|
+
|
@@ -0,0 +1,524 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'web_utils'
|
3
|
+
|
4
|
+
require 'rack/request'
|
5
|
+
require 'rack/mock'
|
6
|
+
|
7
|
+
module FakeModule
|
8
|
+
module FakeSubModule
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe WebUtils do
|
13
|
+
|
14
|
+
parallelize_me!
|
15
|
+
|
16
|
+
let(:utils) { WebUtils }
|
17
|
+
|
18
|
+
describe '#blank?' do
|
19
|
+
describe 'with blank strings' do
|
20
|
+
it 'is true' do
|
21
|
+
['',' '," \n \t"].each do |s|
|
22
|
+
assert utils.blank?(s)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
describe 'with nil' do
|
27
|
+
it('is true') { assert utils.blank?(nil) }
|
28
|
+
end
|
29
|
+
describe 'with non-blank strings' do
|
30
|
+
it 'is false' do
|
31
|
+
['a','abc', ' abc '].each do |s|
|
32
|
+
refute utils.blank?(s)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
describe 'with integer' do
|
37
|
+
it('is false') { refute utils.blank?(1234) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#pluralize' do
|
42
|
+
|
43
|
+
it "Just adds an 's' at the end" do
|
44
|
+
assert_equal 'bags', utils.pluralize('bag')
|
45
|
+
assert_equal 'days', utils.pluralize('day')
|
46
|
+
end
|
47
|
+
describe "The word ends with 'x'" do
|
48
|
+
it "Adds 'es' instead" do
|
49
|
+
assert_equal 'foxes', utils.pluralize('fox')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
describe "The word ends with a consonant and 'y'" do
|
53
|
+
it "Replaces 'y' with 'ie'" do
|
54
|
+
assert_equal 'copies', utils.pluralize('copy')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#singularize' do
|
61
|
+
|
62
|
+
it "Removes the trailing 's'" do
|
63
|
+
assert_equal 'bag', utils.singularize('bags')
|
64
|
+
end
|
65
|
+
describe "The word ends with 'xes'" do
|
66
|
+
it "Removes the 'e' as well" do
|
67
|
+
assert_equal 'fox', utils.singularize('foxes')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
describe "The word ends with 'ies'" do
|
71
|
+
it "Replaces 'ie' with 'y'" do
|
72
|
+
assert_equal 'copy', utils.singularize('copies')
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
describe 'dasherize/undasherize class name' do
|
79
|
+
|
80
|
+
let(:cases) {
|
81
|
+
[
|
82
|
+
['Hello','hello'],
|
83
|
+
['HelloWorld','hello-world'],
|
84
|
+
['HTTPRequest','h-t-t-p-request'],
|
85
|
+
['RestAPI','rest-a-p-i'],
|
86
|
+
['AlteredSMTPAttachment','altered-s-m-t-p-attachment'],
|
87
|
+
['View360', 'view-360'],
|
88
|
+
['View360Degree', 'view-360-degree'],
|
89
|
+
['Degree360::View', 'degree-360--view'],
|
90
|
+
['RestAPI::Request::Post','rest-a-p-i--request--post'],
|
91
|
+
]
|
92
|
+
}
|
93
|
+
|
94
|
+
describe '#dasherize_class_name' do
|
95
|
+
it "Translates correctly" do
|
96
|
+
cases.each do |(classname,dashname)|
|
97
|
+
assert_equal dashname, utils.dasherize_class_name(classname)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe '#undasherize_class_name' do
|
103
|
+
it "Translates correctly" do
|
104
|
+
cases.each do |(classname,dashname)|
|
105
|
+
assert_equal classname, utils.undasherize_class_name(dashname)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
describe '#resolve_class_name' do
|
113
|
+
describe 'when the constant exists' do
|
114
|
+
it 'Returns the constant' do
|
115
|
+
[
|
116
|
+
['String',String],
|
117
|
+
['FakeModule::FakeSubModule',FakeModule::FakeSubModule],
|
118
|
+
].each do |(classname,constant)|
|
119
|
+
assert_equal constant, utils.resolve_class_name(classname)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
describe 'when the constant does not exist' do
|
124
|
+
it 'Raise if the constant does not exist' do
|
125
|
+
['Strang','WebUtils::Yootils','',nil].each do |classname|
|
126
|
+
assert_raises(NameError) do
|
127
|
+
utils.resolve_class_name(classname)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe '#resolve_dasherized_class_name' do
|
135
|
+
it 'Chains both methods for sugar' do
|
136
|
+
assert_equal FakeModule::FakeSubModule, utils.resolve_dasherized_class_name('fake-module--fake-sub-module')
|
137
|
+
assert_equal FakeModule::FakeSubModule, utils.resolve_dasherized_class_name(:'fake-module--fake-sub-module')
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe '#guess_related_class_name' do
|
142
|
+
|
143
|
+
# Used for many things but mainly relationship classes
|
144
|
+
|
145
|
+
describe 'when it starts with a lowercase letter' do
|
146
|
+
it 'Should guess a singular class_name in the context' do
|
147
|
+
assert_equal 'FakeModule::FakeSubModule::RelatedThing', utils.guess_related_class_name(FakeModule::FakeSubModule, :related_things)
|
148
|
+
assert_equal 'FakeModule::FakeSubModule::RelatedThing', utils.guess_related_class_name(FakeModule::FakeSubModule, :related_thing)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
describe 'when it starts with an uppercase letter' do
|
152
|
+
it 'Should return the class_name as-is' do
|
153
|
+
assert_equal 'Class::Given', utils.guess_related_class_name(FakeModule::FakeSubModule, 'Class::Given')
|
154
|
+
end
|
155
|
+
end
|
156
|
+
describe 'when it starts with ::' do
|
157
|
+
it 'Should prepend the class_name whith the context' do
|
158
|
+
assert_equal 'FakeModule::FakeSubModule::RelatedThing', utils.guess_related_class_name(FakeModule::FakeSubModule, '::RelatedThing')
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe '#get_value' do
|
164
|
+
describe 'when arg is a simple object' do
|
165
|
+
it 'Returns it as-is' do
|
166
|
+
assert_equal 'Hello', utils.get_value('Hello')
|
167
|
+
end
|
168
|
+
end
|
169
|
+
describe 'when arg is a proc' do
|
170
|
+
it 'Returns after calling the proc' do
|
171
|
+
assert_equal 'Hello', utils.get_value(proc{'Hello'})
|
172
|
+
end
|
173
|
+
end
|
174
|
+
describe 'when arg is a lambda' do
|
175
|
+
it 'Returns after calling the lambda' do
|
176
|
+
assert_equal 'Hello', utils.get_value(lambda{'Hello'})
|
177
|
+
end
|
178
|
+
end
|
179
|
+
describe 'when arg is a symbol' do
|
180
|
+
describe 'and a context is passed as a second argument' do
|
181
|
+
it 'Sends the message to the context' do
|
182
|
+
assert_equal 'Hello', utils.get_value(:capitalize,'hello')
|
183
|
+
end
|
184
|
+
end
|
185
|
+
describe 'and no context is passed' do
|
186
|
+
it 'Sends the message to Kernel' do
|
187
|
+
assert_equal 'Kernel', utils.get_value(:to_s)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
describe '#deep_copy' do
|
194
|
+
it 'Duplicates the nested objects' do
|
195
|
+
original = {nested_hash: {one: 1}, nested_array: [1]}
|
196
|
+
copy = utils.deep_copy(original)
|
197
|
+
copy[:nested_hash][:one] = 2
|
198
|
+
copy[:nested_array] << 2
|
199
|
+
assert_equal({one: 1}, original[:nested_hash])
|
200
|
+
assert_equal [1], original[:nested_array]
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
describe '#ensure_key!' do
|
205
|
+
let(:arg) { {a: 3} }
|
206
|
+
it 'Sets the key if it did not exist' do
|
207
|
+
utils.ensure_key!(arg,:b,4)
|
208
|
+
assert_equal 4, arg[:b]
|
209
|
+
end
|
210
|
+
it 'Leaves the key untouched if it already existed' do
|
211
|
+
utils.ensure_key!(arg,:a,4)
|
212
|
+
assert_equal 3, arg[:a]
|
213
|
+
end
|
214
|
+
it 'Returns the value of the key' do
|
215
|
+
assert_equal 4, utils.ensure_key!(arg,:b,4)
|
216
|
+
assert_equal 3, utils.ensure_key!(arg,:a,4)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
describe '#ensure_key' do
|
221
|
+
let(:arg) { {a: 3} }
|
222
|
+
it 'Does not change the original' do
|
223
|
+
new_hash = utils.ensure_key(arg,:b,4)
|
224
|
+
refute_equal 4, arg[:b]
|
225
|
+
assert_equal 4, new_hash[:b]
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
describe '#slugify' do
|
230
|
+
|
231
|
+
# For making slug for a document
|
232
|
+
# Possibly used instead of the id
|
233
|
+
|
234
|
+
let(:arg) { "Así es la vida by Daniel Bär & Mickaël ? (100%)" }
|
235
|
+
it 'Builds a string made of lowercase URL-friendly chars' do
|
236
|
+
assert_equal 'asi-es-la-vida-by-daniel-bar-and-mickael-100%25', utils.slugify(arg)
|
237
|
+
end
|
238
|
+
describe 'when second argument is false' do
|
239
|
+
it 'Does not force to lowercase' do
|
240
|
+
assert_equal 'Asi-es-la-vida-by-Daniel-Bar-and-Mickael-100%25', utils.slugify(arg,false)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
describe 'when argument is nil' do
|
244
|
+
let(:arg) { nil }
|
245
|
+
it 'Does not break' do
|
246
|
+
assert_equal '', utils.slugify(arg)
|
247
|
+
assert_equal '', utils.slugify(arg,false)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
describe '#label_for_field' do
|
253
|
+
|
254
|
+
# Returns a friendly name for a field name
|
255
|
+
|
256
|
+
it 'Returns an ideal title case version of the field name' do
|
257
|
+
[
|
258
|
+
['hello', 'Hello'],
|
259
|
+
['hello-world_1234', 'Hello World 1234'],
|
260
|
+
[:hello_world, 'Hello World'],
|
261
|
+
].each do |(arg,result)|
|
262
|
+
assert_equal result, utils.label_for_field(arg)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
describe '#each_stub' do
|
268
|
+
|
269
|
+
# For iterating through end objects of a nested hash/array
|
270
|
+
# It would be used for updating values, typecasting them...
|
271
|
+
|
272
|
+
it 'Yields a block for every stub of a complex object and make changes possible' do
|
273
|
+
before = {
|
274
|
+
'name'=>"BoBBy",
|
275
|
+
'numbers'=>['One','Two'],
|
276
|
+
'meta'=>{'type'=>'Dev','tags'=>['Top','Bottom']}
|
277
|
+
}
|
278
|
+
after = {
|
279
|
+
'name'=>"bobby",
|
280
|
+
'numbers'=>['one','two'],
|
281
|
+
'meta'=>{'type'=>'dev','tags'=>['top','bottom']}
|
282
|
+
}
|
283
|
+
utils.each_stub(before) do |object,key_index,value|
|
284
|
+
object[key_index] = value.to_s.downcase
|
285
|
+
end
|
286
|
+
assert_equal after, before
|
287
|
+
end
|
288
|
+
it 'Raises a TypeError if The object is not appropriate' do
|
289
|
+
[nil,'yo',4].each do |obj|
|
290
|
+
assert_raises(TypeError) do
|
291
|
+
utils.each_stub(obj)
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
describe '#automatic_typecast' do
|
298
|
+
|
299
|
+
# Tries to do automatic typecasting of values
|
300
|
+
# that are received as strings, so most likely
|
301
|
+
# coming from a web form or from a CSV table.
|
302
|
+
#
|
303
|
+
# The purpose is to use it in combination with
|
304
|
+
# #each_stub when a form is received by the API.
|
305
|
+
|
306
|
+
describe 'when a string' do
|
307
|
+
it 'Knows how to convert recognizable datatypes' do
|
308
|
+
[
|
309
|
+
['true',true],
|
310
|
+
['false',false],
|
311
|
+
['',nil],
|
312
|
+
['fack','fack'],
|
313
|
+
['5', 5],
|
314
|
+
['-5', -5],
|
315
|
+
['42', 42],
|
316
|
+
['-42', -42],
|
317
|
+
['99.99', 99.99],
|
318
|
+
['5.0', 5.0],
|
319
|
+
['.4', 0.4],
|
320
|
+
['-99.99', -99.99],
|
321
|
+
['-5.0', -5.0],
|
322
|
+
['-.4', -0.4],
|
323
|
+
['42hello', '42hello'],
|
324
|
+
['5.0hello', '5.0hello']
|
325
|
+
].each do |(arg,result)|
|
326
|
+
assert_equal result, utils.automatic_typecast(arg)
|
327
|
+
end
|
328
|
+
end
|
329
|
+
it 'Can change what is typecasted' do
|
330
|
+
assert_equal '10', utils.automatic_typecast('10', [:bool,:nil])
|
331
|
+
assert_equal true, utils.automatic_typecast('true', [:bool,:nil])
|
332
|
+
end
|
333
|
+
end
|
334
|
+
describe 'when not a string' do
|
335
|
+
it 'Should leave it untouched' do
|
336
|
+
[Time.now,1.0].each do |obj|
|
337
|
+
assert_equal obj, utils.automatic_typecast(obj)
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
describe '#generate_random_id' do
|
344
|
+
it 'Has the correct format' do
|
345
|
+
assert_match /[a-zA-Z0-9]{16}/, utils.generate_random_id
|
346
|
+
end
|
347
|
+
it 'Can have a specific length' do
|
348
|
+
assert_match /[a-zA-Z0-9]{32}/, utils.generate_random_id(32)
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
describe '#nl2br' do
|
353
|
+
it 'Puts unclosed tags by default' do
|
354
|
+
assert_equal '<br>Hello<br>world<br>', utils.nl2br("\nHello\nworld\n")
|
355
|
+
end
|
356
|
+
describe 'with 2nd argument' do
|
357
|
+
it 'Replaces the tag' do
|
358
|
+
assert_equal '<br/>Hello<br/>world<br/>', utils.nl2br("\nHello\nworld\n",'<br/>')
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
describe '#complete_link' do
|
364
|
+
it 'Adds the external double slash when missing' do
|
365
|
+
[
|
366
|
+
['www.web-utils.com','//www.web-utils.com'],
|
367
|
+
['web-utils.com','//web-utils.com'],
|
368
|
+
['please.web-utils.com','//please.web-utils.com'],
|
369
|
+
].each do |(arg,result)|
|
370
|
+
assert_equal result, utils.complete_link(arg)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
it 'Does not alter the url when it does not need double slash' do
|
374
|
+
[
|
375
|
+
['//www.web-utils.com','//www.web-utils.com'],
|
376
|
+
['://www.web-utils.com','://www.web-utils.com'],
|
377
|
+
['http://www.web-utils.com','http://www.web-utils.com'],
|
378
|
+
['ftp://www.web-utils.com','ftp://www.web-utils.com'],
|
379
|
+
['mailto:web@utils.com','mailto:web@utils.com'],
|
380
|
+
['',''],
|
381
|
+
[' ',' '],
|
382
|
+
].each do |(arg,result)|
|
383
|
+
assert_equal result, utils.complete_link(arg)
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
describe '#external_link?' do
|
389
|
+
it 'Returns true when the link would need target=blank' do
|
390
|
+
[
|
391
|
+
['http://web-utils.com', true],
|
392
|
+
['https://web-utils.com', true],
|
393
|
+
['ftp://web-utils.com', true],
|
394
|
+
['://web-utils.com', true],
|
395
|
+
['//web-utils.com', true],
|
396
|
+
['mailto:user@web-utils.com', false],
|
397
|
+
['mailto:user@web-utils.com', false],
|
398
|
+
['/web/utils', false],
|
399
|
+
['web-utils.html', false],
|
400
|
+
].each do |(url,bool)|
|
401
|
+
assert_equal bool, utils.external_link?(url)
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
describe '#automatic_html' do
|
407
|
+
it 'Automates links and line breaks' do
|
408
|
+
input = "Hello\nme@site.co.uk\nNot the begining me@site.co.uk\nme@site.co.uk not the end\nwww.site.co.uk\nVisit www.site.co.uk\nwww.site.co.uk rules\nhttp://www.site.co.uk\nVisit http://www.site.co.uk\nhttp://www.site.co.uk rules"
|
409
|
+
output = "Hello<br><a href='mailto:me@site.co.uk'>me@site.co.uk</a><br>Not the begining <a href='mailto:me@site.co.uk'>me@site.co.uk</a><br><a href='mailto:me@site.co.uk'>me@site.co.uk</a> not the end<br><a href='//www.site.co.uk' target='_blank'>www.site.co.uk</a><br>Visit <a href='//www.site.co.uk' target='_blank'>www.site.co.uk</a><br><a href='//www.site.co.uk' target='_blank'>www.site.co.uk</a> rules<br><a href='http://www.site.co.uk' target='_blank'>http://www.site.co.uk</a><br>Visit <a href='http://www.site.co.uk' target='_blank'>http://www.site.co.uk</a><br><a href='http://www.site.co.uk' target='_blank'>http://www.site.co.uk</a> rules"
|
410
|
+
assert_equal output, utils.automatic_html(input)
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
describe '#truncate' do
|
415
|
+
it 'Truncates to the right amount of letters' do
|
416
|
+
assert_equal 'abc...', utils.truncate('abc defg hijklmnopqrstuvwxyz',3)
|
417
|
+
end
|
418
|
+
it 'Does not cut words' do
|
419
|
+
assert_equal 'abcdefg...', utils.truncate('abcdefg hijklmnopqrstuvwxyz',3)
|
420
|
+
end
|
421
|
+
it 'Removes HTML tags' do
|
422
|
+
assert_equal 'abcdefg...', utils.truncate('<br>abc<a href=#>def</a>g hijklmnopqrstuvwxyz',3)
|
423
|
+
end
|
424
|
+
it 'Does not print the ellipsis if the string is already short enough' do
|
425
|
+
assert_equal 'abc def', utils.truncate('abc def',50)
|
426
|
+
end
|
427
|
+
describe 'with a 3rd argument' do
|
428
|
+
it 'Replaces the ellipsis' do
|
429
|
+
assert_equal 'abc!', utils.truncate('abc defg hijklmnopqrstuvwxyz',3,'!')
|
430
|
+
assert_equal 'abc', utils.truncate('abc defg hijklmnopqrstuvwxyz',3,'')
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
describe '#display_price' do
|
436
|
+
it 'Turns a price number in cents/pence into a displayable one' do
|
437
|
+
assert_equal '45.95', utils.display_price(4595)
|
438
|
+
end
|
439
|
+
it 'Removes cents if it is 00' do
|
440
|
+
assert_equal '70', utils.display_price(7000)
|
441
|
+
end
|
442
|
+
it 'Adds comma delimiters on thousands' do
|
443
|
+
assert_equal '12,345,678.90', utils.display_price(1234567890)
|
444
|
+
end
|
445
|
+
it 'Works with negative numbers' do
|
446
|
+
assert_equal '-1,400', utils.display_price(-140000)
|
447
|
+
end
|
448
|
+
it 'Raises when argument is not int' do
|
449
|
+
assert_raises(TypeError) do
|
450
|
+
utils.display_price('abc')
|
451
|
+
end
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
describe '#parse_price' do
|
456
|
+
it 'Parses a string and find the price in cents/pence' do
|
457
|
+
assert_equal 4595, utils.parse_price('45.95')
|
458
|
+
end
|
459
|
+
it 'Works when you omit the cents/pence' do
|
460
|
+
assert_equal 2800, utils.parse_price('28')
|
461
|
+
end
|
462
|
+
it 'Ignores visual help but works with negative prices' do
|
463
|
+
assert_equal -1234567890, utils.parse_price(' £-12,345,678.90 ')
|
464
|
+
end
|
465
|
+
it 'Raises when argument is not string' do
|
466
|
+
assert_raises(TypeError) do
|
467
|
+
utils.parse_price(42)
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
describe '#branded_filename' do
|
473
|
+
it 'Adds WebUtils to the file name' do
|
474
|
+
assert_equal "/path/to/WebUtils-file.png", utils.branded_filename("/path/to/file.png")
|
475
|
+
end
|
476
|
+
it 'Works when there is just a file name' do
|
477
|
+
assert_equal "WebUtils-file.png", utils.branded_filename("file.png")
|
478
|
+
end
|
479
|
+
it 'Can change the brand' do
|
480
|
+
assert_equal "/path/to/Brand-file.png", utils.branded_filename("/path/to/file.png",'Brand')
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
describe '#filename_variation' do
|
485
|
+
it 'Replaces the ext with variation name and new ext' do
|
486
|
+
assert_equal "/path/to/file.thumb.gif", utils.filename_variation("/path/to/file.png", :thumb, :gif)
|
487
|
+
end
|
488
|
+
it 'Works when there is just a filename' do
|
489
|
+
assert_equal "file.thumb.gif", utils.filename_variation("file.png", :thumb, :gif)
|
490
|
+
end
|
491
|
+
it "Works when there is no ext to start with" do
|
492
|
+
assert_equal "/path/to/file.thumb.gif", utils.filename_variation("/path/to/file", :thumb, :gif)
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
describe '#initial_request?' do
|
497
|
+
let(:req) {
|
498
|
+
Rack::Request.new(
|
499
|
+
Rack::MockRequest.env_for(
|
500
|
+
'/path',
|
501
|
+
{'HTTP_REFERER'=>referer}
|
502
|
+
)
|
503
|
+
)
|
504
|
+
}
|
505
|
+
let(:referer) { nil }
|
506
|
+
it 'Returns true' do
|
507
|
+
assert utils.initial_request?(req)
|
508
|
+
end
|
509
|
+
describe 'Request comes from another domain' do
|
510
|
+
let(:referer) { 'https://www.google.com/path' }
|
511
|
+
it 'Returns true' do
|
512
|
+
assert utils.initial_request?(req)
|
513
|
+
end
|
514
|
+
end
|
515
|
+
describe 'Request comes from same domain' do
|
516
|
+
let(:referer) { 'http://example.org' }
|
517
|
+
it 'Returns false' do
|
518
|
+
refute utils.initial_request?(req)
|
519
|
+
end
|
520
|
+
end
|
521
|
+
end
|
522
|
+
|
523
|
+
end
|
524
|
+
|
data/web-utils.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
|
3
|
+
s.authors = ["Mickael Riga"]
|
4
|
+
s.email = ["mig@mypeplum.com"]
|
5
|
+
s.homepage = "https://github.com/mig-hub/web-utils"
|
6
|
+
s.licenses = ['MIT']
|
7
|
+
|
8
|
+
s.name = 'web-utils'
|
9
|
+
s.version = '0.0.1'
|
10
|
+
s.summary = "Web Utils"
|
11
|
+
s.description = "Useful web-related helper methods for models, views or controllers."
|
12
|
+
|
13
|
+
s.platform = Gem::Platform::RUBY
|
14
|
+
s.files = `git ls-files`.split("\n").sort
|
15
|
+
s.test_files = s.files.grep(/^test\//)
|
16
|
+
s.require_paths = ['lib']
|
17
|
+
s.add_dependency('rack', '~> 0')
|
18
|
+
|
19
|
+
end
|
20
|
+
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: web-utils
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mickael Riga
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-10-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: Useful web-related helper methods for models, views or controllers.
|
28
|
+
email:
|
29
|
+
- mig@mypeplum.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- ".gitignore"
|
35
|
+
- Gemfile
|
36
|
+
- LICENSE
|
37
|
+
- README.md
|
38
|
+
- lib/web_utils.rb
|
39
|
+
- test/test_web_utils.rb
|
40
|
+
- web-utils.gemspec
|
41
|
+
homepage: https://github.com/mig-hub/web-utils
|
42
|
+
licenses:
|
43
|
+
- MIT
|
44
|
+
metadata: {}
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options: []
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
requirements: []
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 2.5.1
|
62
|
+
signing_key:
|
63
|
+
specification_version: 4
|
64
|
+
summary: Web Utils
|
65
|
+
test_files:
|
66
|
+
- test/test_web_utils.rb
|