smartname 0.4.0 → 0.5.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.
- checksums.yaml +4 -4
- data/Gemfile.lock +21 -19
- data/VERSION +1 -1
- data/lib/smart_name.rb +32 -223
- data/lib/smart_name/contextual.rb +113 -0
- data/lib/smart_name/manipulate.rb +73 -0
- data/lib/smart_name/parts.rb +124 -0
- data/lib/smart_name/predicates.rb +39 -0
- data/lib/smart_name/variants.rb +36 -0
- data/spec/lib/smart_name/contextual_spec.rb +133 -0
- data/spec/lib/smart_name/manipulate_spec.rb +75 -0
- data/spec/lib/smart_name/parts_spec.rb +65 -0
- data/spec/lib/smart_name/variants_spec.rb +44 -0
- data/spec/lib/smart_name_spec.rb +50 -183
- data/spec/spec_helper.rb +1 -0
- metadata +11 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: db3a78f4a254d599ca83a93a19d14a61a37c8b05
|
4
|
+
data.tar.gz: 1889454b39276d368ea7ae05b53e255c9f559490
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa470ffc741acea8d1d3b3e22c9a61d8b79de9f2f2650c5f08d669e7920bc1c237cdd4310f6cbfef8ab8ccde0243a64dfc07cfd4895bbdafe14b19aa5f9ff8cf
|
7
|
+
data.tar.gz: 50be6deee20d1b2c0554645786be70167ceaf8b41147fe982dc42fac5d59d84290ffc1952c39e5a05251bada945244534817bf532ba3e0a151d3963d86902cc7
|
data/Gemfile.lock
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
-
activesupport (5.0.
|
4
|
+
activesupport (5.0.1)
|
5
5
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
6
6
|
i18n (~> 0.7)
|
7
7
|
minitest (~> 5.1)
|
8
8
|
tzinfo (~> 1.1)
|
9
|
-
addressable (2.
|
10
|
-
|
11
|
-
|
9
|
+
addressable (2.5.0)
|
10
|
+
public_suffix (~> 2.0, >= 2.0.2)
|
11
|
+
builder (3.2.3)
|
12
|
+
concurrent-ruby (1.0.4)
|
12
13
|
descendants_tracker (0.0.4)
|
13
14
|
thread_safe (~> 0.3, >= 0.3.1)
|
14
|
-
diff-lcs (1.
|
15
|
+
diff-lcs (1.3)
|
15
16
|
faraday (0.9.2)
|
16
17
|
multipart-post (>= 1.2, < 3)
|
17
18
|
git (1.3.0)
|
@@ -23,39 +24,40 @@ GEM
|
|
23
24
|
multi_json (>= 1.7.5, < 2.0)
|
24
25
|
nokogiri (~> 1.6.0)
|
25
26
|
oauth2
|
26
|
-
hashie (3.4
|
27
|
+
hashie (3.5.4)
|
27
28
|
highline (1.7.8)
|
28
29
|
htmlentities (4.3.4)
|
29
|
-
i18n (0.
|
30
|
-
jeweler (2.
|
30
|
+
i18n (0.8.1)
|
31
|
+
jeweler (2.3.3)
|
31
32
|
builder
|
32
33
|
bundler (>= 1.0)
|
33
34
|
git (>= 1.2.5)
|
34
35
|
github_api (~> 0.11.0)
|
35
36
|
highline (>= 1.6.15)
|
36
37
|
nokogiri (>= 1.5.10)
|
38
|
+
psych (~> 2.2)
|
37
39
|
rake
|
38
40
|
rdoc
|
39
|
-
|
40
|
-
json (1.8.3)
|
41
|
+
semver2
|
41
42
|
jwt (1.5.6)
|
42
43
|
mini_portile2 (2.1.0)
|
43
|
-
minitest (5.
|
44
|
+
minitest (5.10.1)
|
44
45
|
multi_json (1.12.1)
|
45
|
-
multi_xml (0.
|
46
|
+
multi_xml (0.6.0)
|
46
47
|
multipart-post (2.0.0)
|
47
48
|
nokogiri (1.6.8.1)
|
48
49
|
mini_portile2 (~> 2.1.0)
|
49
|
-
oauth2 (1.
|
50
|
-
faraday (>= 0.8, < 0.
|
50
|
+
oauth2 (1.3.0)
|
51
|
+
faraday (>= 0.8, < 0.11)
|
51
52
|
jwt (~> 1.0)
|
52
53
|
multi_json (~> 1.3)
|
53
54
|
multi_xml (~> 0.5)
|
54
55
|
rack (>= 1.2, < 3)
|
56
|
+
psych (2.2.4)
|
57
|
+
public_suffix (2.0.5)
|
55
58
|
rack (2.0.1)
|
56
|
-
rake (
|
57
|
-
rdoc (
|
58
|
-
json (~> 1.4)
|
59
|
+
rake (12.0.0)
|
60
|
+
rdoc (5.1.0)
|
59
61
|
rspec (3.5.0)
|
60
62
|
rspec-core (~> 3.5.0)
|
61
63
|
rspec-expectations (~> 3.5.0)
|
@@ -69,8 +71,8 @@ GEM
|
|
69
71
|
diff-lcs (>= 1.2.0, < 2.0)
|
70
72
|
rspec-support (~> 3.5.0)
|
71
73
|
rspec-support (3.5.0)
|
72
|
-
|
73
|
-
thread_safe (0.3.
|
74
|
+
semver2 (3.4.2)
|
75
|
+
thread_safe (0.3.6)
|
74
76
|
tzinfo (1.2.2)
|
75
77
|
thread_safe (~> 0.1)
|
76
78
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.5.0
|
data/lib/smart_name.rb
CHANGED
@@ -3,41 +3,61 @@
|
|
3
3
|
require 'active_support/configurable'
|
4
4
|
require 'active_support/inflector'
|
5
5
|
require 'htmlentities'
|
6
|
+
require_relative 'smart_name/parts'
|
7
|
+
require_relative 'smart_name/variants'
|
8
|
+
require_relative 'smart_name/contextual'
|
9
|
+
require_relative 'smart_name/predicates'
|
10
|
+
require_relative 'smart_name/manipulate'
|
6
11
|
|
7
12
|
class SmartName < Object
|
13
|
+
|
14
|
+
include Parts
|
15
|
+
include Variants
|
16
|
+
include Contextual
|
17
|
+
include Predicates
|
18
|
+
include Manipulate
|
19
|
+
|
8
20
|
RUBYENCODING = RUBY_VERSION !~ /^1\.8/
|
9
21
|
OK4KEY_RE = RUBYENCODING ? '\p{Word}\*' : '\w\*'
|
10
22
|
|
11
23
|
include ActiveSupport::Configurable
|
12
24
|
|
13
|
-
config_accessor :joint, :banned_array, :var_re, :uninflect, :params,
|
25
|
+
config_accessor :joint, :banned_array, :var_re, :uninflect, :params,
|
26
|
+
:session, :stabilize
|
14
27
|
|
15
28
|
SmartName.joint = '+'
|
16
29
|
SmartName.banned_array = ['/', '~', '|']
|
17
30
|
SmartName.var_re = /\{([^\}]*\})\}/
|
18
31
|
SmartName.uninflect = :singularize
|
19
|
-
SmartName.stabilize = false
|
20
|
-
|
32
|
+
SmartName.stabilize = false
|
21
33
|
|
22
34
|
JOINT_RE = Regexp.escape joint
|
23
35
|
|
24
|
-
@@name2nameobject = {}
|
36
|
+
@@name2nameobject = {} # name cache
|
25
37
|
|
26
38
|
class << self
|
27
39
|
def new obj
|
28
40
|
return obj if obj.is_a? self.class
|
29
|
-
str =
|
30
|
-
|
31
|
-
|
41
|
+
str = stringify obj
|
42
|
+
known_name(str) || super(str.strip)
|
43
|
+
end
|
44
|
+
|
45
|
+
def known_name str
|
46
|
+
@@name2nameobject[str]
|
47
|
+
end
|
48
|
+
|
49
|
+
def stringify obj
|
50
|
+
if obj.is_a?(Array)
|
51
|
+
obj.map(&:to_s) * joint
|
32
52
|
else
|
33
|
-
|
53
|
+
obj.to_s
|
34
54
|
end
|
35
55
|
end
|
36
56
|
|
37
57
|
def banned_re
|
38
58
|
%r{#{ (['['] + banned_array << joint) * '\\' + ']' }}
|
39
59
|
end
|
40
|
-
|
60
|
+
|
41
61
|
# Sometimes the core rule "the key's key must be itself" (called "stable" below) is violated
|
42
62
|
# eg. it fails with singularize as uninflect method for Matthias -> Matthia -> Matthium
|
43
63
|
# Usually that means the name is a proper noun and not a plural.
|
@@ -55,23 +75,14 @@ class SmartName < Object
|
|
55
75
|
|
56
76
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
57
77
|
# ~~~~~~~~~~~~~~~~~~~~~~ INSTANCE ~~~~~~~~~~~~~~~~~~~~~~~~~
|
58
|
-
|
59
|
-
attr_reader :simple, :parts, :key, :s
|
78
|
+
attr_reader :key, :s
|
60
79
|
alias to_s s
|
61
80
|
|
62
81
|
def initialize str
|
63
82
|
@s = str.to_s.strip
|
64
83
|
@s = @s.encode('UTF-8') if RUBYENCODING
|
65
|
-
|
66
|
-
|
67
|
-
@parts << '' if @s[-1, 1] == self.class.joint
|
68
|
-
@simple = false
|
69
|
-
@parts.map { |p| p.to_name.key } * self.class.joint
|
70
|
-
else
|
71
|
-
@parts = [str]
|
72
|
-
@simple = true
|
73
|
-
str.empty? ? '' : simple_key
|
74
|
-
end
|
84
|
+
initialize_parts
|
85
|
+
@key = @part_keys.join(self.class.joint)
|
75
86
|
@@name2nameobject[str] = self
|
76
87
|
end
|
77
88
|
|
@@ -87,17 +98,6 @@ class SmartName < Object
|
|
87
98
|
to_s.size
|
88
99
|
end
|
89
100
|
|
90
|
-
def blank?
|
91
|
-
s.blank?
|
92
|
-
end
|
93
|
-
alias empty? blank?
|
94
|
-
|
95
|
-
def valid?
|
96
|
-
!parts.find do |pt|
|
97
|
-
pt.match self.class.banned_re
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
101
|
def inspect
|
102
102
|
"<#{self.class.name} key=#{key}[#{self}]>"
|
103
103
|
end
|
@@ -111,195 +111,4 @@ class SmartName < Object
|
|
111
111
|
end
|
112
112
|
other_key == key
|
113
113
|
end
|
114
|
-
|
115
|
-
# ~~~~~~~~~~~~~~~~~~~ VARIANTS ~~~~~~~~~~~~~~~~~~~
|
116
|
-
|
117
|
-
def simple_key
|
118
|
-
decoded
|
119
|
-
.underscore
|
120
|
-
.gsub(/[^#{OK4KEY_RE}]+/, '_')
|
121
|
-
.split(/_+/)
|
122
|
-
.reject(&:empty?)
|
123
|
-
.map { |key| SmartName.stable_uninflect(key) }
|
124
|
-
.join('_')
|
125
|
-
end
|
126
|
-
|
127
|
-
def url_key
|
128
|
-
@url_key ||= part_names.map do |part_name|
|
129
|
-
stripped = part_name.decoded.gsub(/[^#{OK4KEY_RE}]+/, ' ').strip
|
130
|
-
stripped.gsub(/[\s\_]+/, '_')
|
131
|
-
end * self.class.joint
|
132
|
-
end
|
133
|
-
|
134
|
-
def safe_key
|
135
|
-
@safe_key ||= key.tr('*', 'X').tr self.class.joint, '-'
|
136
|
-
end
|
137
|
-
|
138
|
-
def decoded
|
139
|
-
@decoded ||= s.index('&') ? HTMLEntities.new.decode(s) : s
|
140
|
-
end
|
141
|
-
|
142
|
-
# ~~~~~~~~~~~~~~~~~~~ PARTS ~~~~~~~~~~~~~~~~~~~
|
143
|
-
|
144
|
-
alias simple? simple
|
145
|
-
def junction?
|
146
|
-
!simple?
|
147
|
-
end
|
148
|
-
|
149
|
-
def left
|
150
|
-
@left ||= simple? ? nil : parts[0..-2] * self.class.joint
|
151
|
-
end
|
152
|
-
|
153
|
-
def right
|
154
|
-
@right ||= simple? ? nil : parts[-1]
|
155
|
-
end
|
156
|
-
|
157
|
-
def left_name
|
158
|
-
@left_name ||= left && self.class.new(left)
|
159
|
-
end
|
160
|
-
|
161
|
-
def right_name
|
162
|
-
@right_name ||= right && self.class.new(right)
|
163
|
-
end
|
164
|
-
|
165
|
-
# Note that all names have a trunk and tag,
|
166
|
-
# but only junctions have left and right
|
167
|
-
|
168
|
-
def trunk
|
169
|
-
@trunk ||= simple? ? s : left
|
170
|
-
end
|
171
|
-
|
172
|
-
def tag
|
173
|
-
@tag ||= simple? ? s : right
|
174
|
-
end
|
175
|
-
|
176
|
-
def trunk_name
|
177
|
-
@trunk_name ||= simple? ? self : left_name
|
178
|
-
end
|
179
|
-
|
180
|
-
def tag_name
|
181
|
-
@tag_name ||= simple? ? self : right_name
|
182
|
-
end
|
183
|
-
|
184
|
-
def part_names
|
185
|
-
@part_names ||= parts.map(&:to_name)
|
186
|
-
end
|
187
|
-
|
188
|
-
def piece_names
|
189
|
-
@piece_names ||= pieces.map(&:to_name)
|
190
|
-
end
|
191
|
-
|
192
|
-
def pieces
|
193
|
-
@pieces ||=
|
194
|
-
if simple?
|
195
|
-
[self]
|
196
|
-
else
|
197
|
-
junction_pieces = []
|
198
|
-
parts[1..-1].inject parts[0] do |left, right|
|
199
|
-
piece = [left, right] * self.class.joint
|
200
|
-
junction_pieces << piece
|
201
|
-
piece
|
202
|
-
end
|
203
|
-
parts + junction_pieces
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
# ~~~~~~~~~~~~~~~~~~~~ SHOW / ABSOLUTE ~~~~~~~~~~~~~~~~~~~~
|
208
|
-
|
209
|
-
def to_show *ignore
|
210
|
-
ignore.map!(&:to_name)
|
211
|
-
|
212
|
-
show_parts = parts.map do |part|
|
213
|
-
reject = (part.empty? || (part =~ /^_/) || ignore.member?(part.to_name))
|
214
|
-
reject ? nil : part
|
215
|
-
end
|
216
|
-
|
217
|
-
show_name = show_parts.compact.to_name.s
|
218
|
-
|
219
|
-
case
|
220
|
-
when show_parts.compact.empty? then self
|
221
|
-
when show_parts[0].nil? then self.class.joint + show_name
|
222
|
-
else show_name
|
223
|
-
end
|
224
|
-
end
|
225
|
-
|
226
|
-
def to_absolute context, args={}
|
227
|
-
context = context.to_name
|
228
|
-
parts.map do |part|
|
229
|
-
new_part =
|
230
|
-
case part
|
231
|
-
when /^_user$/i
|
232
|
-
name_proc = self.class.session
|
233
|
-
name_proc ? name_proc.call : part
|
234
|
-
when /^_main$/i then self.class.params[:main_name]
|
235
|
-
when /^(_self|_whole|_)$/i then context.s
|
236
|
-
when /^_left$/i then context.trunk
|
237
|
-
# note - inconsistent use of left v. trunk
|
238
|
-
when /^_right$/i then context.tag
|
239
|
-
when /^_(\d+)$/i
|
240
|
-
pos = $~[1].to_i
|
241
|
-
pos = context.length if pos > context.length
|
242
|
-
context.parts[pos - 1]
|
243
|
-
when /^_(L*)(R?)$/i
|
244
|
-
l_s, r_s = $~[1].size, !$~[2].empty?
|
245
|
-
l_part = context.nth_left l_s
|
246
|
-
r_s ? l_part.tag : l_part.s
|
247
|
-
# when /^_/
|
248
|
-
# custom = args[:params] ? args[:params][part] : nil
|
249
|
-
# custom ? CGI.escapeHTML(custom) : part
|
250
|
-
# why are we escaping HTML here?
|
251
|
-
else
|
252
|
-
part
|
253
|
-
end.to_s.strip
|
254
|
-
new_part.empty? ? context.to_s : new_part
|
255
|
-
end * self.class.joint
|
256
|
-
end
|
257
|
-
|
258
|
-
def to_absolute_name *args
|
259
|
-
self.class.new to_absolute(*args)
|
260
|
-
end
|
261
|
-
|
262
|
-
def nth_left n
|
263
|
-
# 1 = left; 2= left of left; 3 = left of left of left....
|
264
|
-
(n >= length ? parts[0] : parts[0..-n - 1]).to_name
|
265
|
-
end
|
266
|
-
|
267
|
-
# ~~~~~~~~~~~~~~~~~~~~ MISC ~~~~~~~~~~~~~~~~~~~~
|
268
|
-
|
269
|
-
def replace_part oldpart, newpart
|
270
|
-
oldpart = oldpart.to_name
|
271
|
-
newpart = newpart.to_name
|
272
|
-
if oldpart.simple?
|
273
|
-
if simple?
|
274
|
-
self == oldpart ? newpart : self
|
275
|
-
else
|
276
|
-
parts.map do |p|
|
277
|
-
oldpart == p ? newpart.to_s : p
|
278
|
-
end.to_name
|
279
|
-
end
|
280
|
-
elsif simple?
|
281
|
-
self
|
282
|
-
else
|
283
|
-
if oldpart == parts[0, oldpart.length]
|
284
|
-
if length == oldpart.length
|
285
|
-
newpart
|
286
|
-
else
|
287
|
-
(newpart.parts + parts[oldpart.length..-1]).to_name
|
288
|
-
end
|
289
|
-
else
|
290
|
-
self
|
291
|
-
end
|
292
|
-
end
|
293
|
-
end
|
294
|
-
|
295
|
-
# HACK. This doesn't belong here.
|
296
|
-
# shouldn't it use inclusions???
|
297
|
-
def self.substitute! str, hash
|
298
|
-
hash.keys.each do |var|
|
299
|
-
str.gsub! var_re do |x|
|
300
|
-
hash[var.to_sym]
|
301
|
-
end
|
302
|
-
end
|
303
|
-
str
|
304
|
-
end
|
305
114
|
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
class SmartName
|
2
|
+
module Contextual
|
3
|
+
RELATIVE_REGEXP = /\b_(left|right|whole|self|user|main|\d+|L*R?)\b/
|
4
|
+
|
5
|
+
def relative_name context_name
|
6
|
+
to_show(*context_name.to_name.parts).to_name
|
7
|
+
end
|
8
|
+
|
9
|
+
def absolute_name context_name
|
10
|
+
to_absolute_name(context_name)
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return true if name is left or right of context
|
14
|
+
def child_of? context
|
15
|
+
return false unless junction?
|
16
|
+
context_key = context.to_name.key
|
17
|
+
absolute_name(context).parent_keys.include? context_key
|
18
|
+
end
|
19
|
+
|
20
|
+
def relative?
|
21
|
+
s =~ RELATIVE_REGEXP || starts_with_joint?
|
22
|
+
end
|
23
|
+
|
24
|
+
def simple_relative?
|
25
|
+
relative? && stripped.to_name.starts_with_joint?
|
26
|
+
end
|
27
|
+
|
28
|
+
def absolute?
|
29
|
+
!relative?
|
30
|
+
end
|
31
|
+
|
32
|
+
def stripped
|
33
|
+
s.gsub RELATIVE_REGEXP, ""
|
34
|
+
end
|
35
|
+
|
36
|
+
def starts_with_joint?
|
37
|
+
length >= 2 && parts.first.empty?
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_show *ignore
|
41
|
+
ignore.map!(&:to_name)
|
42
|
+
|
43
|
+
show_parts = parts.map do |part|
|
44
|
+
reject = (part.empty? || (part =~ /^_/) || ignore.member?(part.to_name))
|
45
|
+
reject ? nil : part
|
46
|
+
end
|
47
|
+
|
48
|
+
show_name = show_parts.compact.to_name.s
|
49
|
+
|
50
|
+
case
|
51
|
+
when show_parts.compact.empty? then self
|
52
|
+
when show_parts[0].nil? then self.class.joint + show_name
|
53
|
+
else show_name
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_absolute context, args={}
|
58
|
+
context = context.to_name
|
59
|
+
|
60
|
+
new_parts = replace_contextual_parts context
|
61
|
+
|
62
|
+
if new_parts.first.empty? && !new_parts.to_name.starts_with?(context)
|
63
|
+
new_parts[0] = context.to_s
|
64
|
+
end
|
65
|
+
if new_parts.last.empty? && !new_parts.to_name.ends_with?(context)
|
66
|
+
new_parts[-1] = context.to_s
|
67
|
+
end
|
68
|
+
new_parts.join self.class.joint
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_absolute_name *args
|
72
|
+
self.class.new to_absolute(*args)
|
73
|
+
end
|
74
|
+
|
75
|
+
def nth_left n
|
76
|
+
# 1 = left; 2= left of left; 3 = left of left of left....
|
77
|
+
(n >= length ? parts[0] : parts[0..-n - 1]).to_name
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def replace_contextual_parts context
|
83
|
+
parts.map do |part|
|
84
|
+
new_part =
|
85
|
+
case part
|
86
|
+
when /^_user$/i
|
87
|
+
name_proc = self.class.session
|
88
|
+
name_proc ? name_proc.call : part
|
89
|
+
when /^_main$/i then self.class.params[:main_name]
|
90
|
+
when /^(_self|_whole|_)$/i then context.s
|
91
|
+
when /^_left$/i then context.trunk
|
92
|
+
# note - inconsistent use of left v. trunk
|
93
|
+
when /^_right$/i then context.tag
|
94
|
+
when /^_(\d+)$/i
|
95
|
+
pos = $~[1].to_i
|
96
|
+
pos = context.length if pos > context.length
|
97
|
+
context.parts[pos - 1]
|
98
|
+
when /^_(L*)(R?)$/i
|
99
|
+
l_s, r_s = $~[1].size, !$~[2].empty?
|
100
|
+
l_part = context.nth_left l_s
|
101
|
+
r_s ? l_part.tag : l_part.s
|
102
|
+
# when /^_/
|
103
|
+
# custom = args[:params] ? args[:params][part] : nil
|
104
|
+
# custom ? CGI.escapeHTML(custom) : part
|
105
|
+
# why are we escaping HTML here?
|
106
|
+
else
|
107
|
+
part
|
108
|
+
end.to_s.strip
|
109
|
+
new_part
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|