web-utils 0.0.1
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 +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
|