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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 25ae787d7849fe77b765c1465ed2b1a534f8206a
4
- data.tar.gz: c27cf085aecc946cb2a40fdd3fd1489a1c146efc
3
+ metadata.gz: db3a78f4a254d599ca83a93a19d14a61a37c8b05
4
+ data.tar.gz: 1889454b39276d368ea7ae05b53e255c9f559490
5
5
  SHA512:
6
- metadata.gz: e72752157b7207b45fc2633311183a66522a9115c39618be0f5b309e86a333a1ce8c2aab2be7cfb7884384f61dfd4419547de8b8c0f9df619e5fedcf05407d75
7
- data.tar.gz: db06e0e3472152b197177d663ee67132b29397b643beebaa6049470cf945908dd07da0f3bff3b7d9fa37c0b870551347ffb181c4c6ec538d718ef4f350417598
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.0.1)
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.4.0)
10
- builder (3.2.2)
11
- concurrent-ruby (1.0.2)
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.2.5)
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.6)
27
+ hashie (3.5.4)
27
28
  highline (1.7.8)
28
29
  htmlentities (4.3.4)
29
- i18n (0.7.0)
30
- jeweler (2.1.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
- semver
40
- json (1.8.3)
41
+ semver2
41
42
  jwt (1.5.6)
42
43
  mini_portile2 (2.1.0)
43
- minitest (5.9.1)
44
+ minitest (5.10.1)
44
45
  multi_json (1.12.1)
45
- multi_xml (0.5.5)
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.2.0)
50
- faraday (>= 0.8, < 0.10)
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 (11.3.0)
57
- rdoc (4.2.2)
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
- semver (1.0.1)
73
- thread_safe (0.3.5)
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.4.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, :session, :stabilize
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 = obj.is_a?(Array) ? obj * joint : obj.to_s
30
- if (known_name = @@name2nameobject[str])
31
- known_name
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
- super str.strip
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
- @key = if @s.index(self.class.joint)
66
- @parts = @s.split(/\s*#{JOINT_RE}\s*/)
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