smartname 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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