cardname 0.15.0 → 0.15.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 +4 -4
- data/lib/cardname/class_methods.rb +60 -0
- data/lib/cardname/contextual.rb +42 -28
- data/lib/cardname/manipulate.rb +34 -43
- data/lib/cardname/parts.rb +109 -35
- data/lib/cardname/pieces.rb +16 -4
- data/lib/cardname/predicates.rb +7 -5
- data/lib/cardname/variants.rb +5 -4
- data/lib/cardname.rb +29 -52
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 31233d7951221846f2898d40721963a9fe71614d08e5c395faae832546066b61
|
|
4
|
+
data.tar.gz: b3bbaf9b81dac6212c3434604204d700eb99dfa684dcb13bd6503892ac6219a9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1c30f90fb867e3fb83279c46b55976fdcd96301b744517a56e9aee4427e3562e45ff3fda00b28c416913312e9a1b02008299b6958aaad09bcdc5fa1dcea2e8ee
|
|
7
|
+
data.tar.gz: 8482990c4776c0fa2529309d5116fefd28f03a3f32561335c09c3581b8dac5355240c72507a788903cec46eb4884285d790696abd3775136fac54c863acd316c
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
class Cardname
|
|
2
|
+
# methods for the Cardname class.
|
|
3
|
+
module ClassMethods
|
|
4
|
+
# #new skips installation and returns a cached Cardname object when possible
|
|
5
|
+
# @return [Cardname]
|
|
6
|
+
def new obj
|
|
7
|
+
return obj if obj.is_a? self.class
|
|
8
|
+
|
|
9
|
+
str = stringify(obj)
|
|
10
|
+
cache[str] ||= super(str)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Cardname cache. keys are strings, values are corresponding Cardname objects
|
|
14
|
+
# Note that unlike most decko/card caches, the cardname cache is process-specific
|
|
15
|
+
# and should not need to be reset even with data changes, because Cardname objects
|
|
16
|
+
# are not data-aware.
|
|
17
|
+
#
|
|
18
|
+
# @return [Hash]
|
|
19
|
+
def cache
|
|
20
|
+
@cache ||= {}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# reset Cardname cache
|
|
24
|
+
# @see #cache
|
|
25
|
+
# @return [Hash]
|
|
26
|
+
def reset
|
|
27
|
+
@cache = {}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# true if there are no banned characters
|
|
31
|
+
# @return [Boolean]
|
|
32
|
+
def nothing_banned?
|
|
33
|
+
return @nothing_banned unless @nothing_banned.nil?
|
|
34
|
+
|
|
35
|
+
@nothing_banned = banned_array.empty?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# regular expression for detecting banned characters
|
|
39
|
+
# @return [Regexp]
|
|
40
|
+
def banned_re
|
|
41
|
+
@banned_re ||= /[#{Regexp.escape((banned_array + [joint])).join}]/
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# split string on joint into parts
|
|
45
|
+
# @return [Array]
|
|
46
|
+
def split_parts str
|
|
47
|
+
str.split(/\s*#{JOINT_RE}\s*/, -1)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def stringify obj
|
|
53
|
+
if obj.is_a?(Array)
|
|
54
|
+
obj.map(&:to_s) * joint
|
|
55
|
+
else
|
|
56
|
+
obj.to_s
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
data/lib/cardname/contextual.rb
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
class Cardname
|
|
2
|
+
# contextual (or relative) names are names that vary by context
|
|
2
3
|
module Contextual
|
|
3
4
|
RELATIVE_REGEXP = /\b_(left|right|whole|self|user|main|\d+|L*R?)\b/
|
|
4
5
|
|
|
5
|
-
# @return true if name is left or right of context
|
|
6
|
+
# @return [Boolean] true if name is left or right of context
|
|
6
7
|
def child_of? context
|
|
7
8
|
return false unless compound?
|
|
8
9
|
|
|
@@ -10,22 +11,31 @@ class Cardname
|
|
|
10
11
|
absolute_name(context).parent_keys.include? context_key
|
|
11
12
|
end
|
|
12
13
|
|
|
14
|
+
# @return [Boolean]
|
|
13
15
|
def relative?
|
|
14
16
|
starts_with_joint? || (s =~ RELATIVE_REGEXP).present?
|
|
15
17
|
end
|
|
16
18
|
|
|
19
|
+
# starts with joint, no other contextual element
|
|
20
|
+
# @return [Boolean]
|
|
17
21
|
def simple_relative?
|
|
18
22
|
starts_with_joint? && (s =~ RELATIVE_REGEXP).nil?
|
|
19
23
|
end
|
|
20
24
|
|
|
25
|
+
# not relative
|
|
26
|
+
# @return [Boolean]
|
|
21
27
|
def absolute?
|
|
22
28
|
!relative?
|
|
23
29
|
end
|
|
24
30
|
|
|
31
|
+
# contextual elements removed
|
|
32
|
+
# @return [String]
|
|
25
33
|
def stripped
|
|
26
34
|
s.gsub RELATIVE_REGEXP, ""
|
|
27
35
|
end
|
|
28
36
|
|
|
37
|
+
# +X
|
|
38
|
+
# @return [Boolean]
|
|
29
39
|
def starts_with_joint?
|
|
30
40
|
compound? && parts.first.empty?
|
|
31
41
|
end
|
|
@@ -44,22 +54,46 @@ class Cardname
|
|
|
44
54
|
key == compressed.absolute_name(from).key ? compressed : self
|
|
45
55
|
end
|
|
46
56
|
|
|
47
|
-
|
|
48
|
-
|
|
57
|
+
# @return [String]
|
|
58
|
+
def absolute context
|
|
59
|
+
context = (context || "").to_name
|
|
60
|
+
new_parts = absolutize_contextual_parts context
|
|
61
|
+
return "" if new_parts.empty?
|
|
49
62
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
63
|
+
absolutize_extremes new_parts, context.s
|
|
64
|
+
new_parts.join self.class.joint
|
|
65
|
+
end
|
|
53
66
|
|
|
54
|
-
|
|
67
|
+
# @return [Cardname]
|
|
68
|
+
def absolute_name *args
|
|
69
|
+
absolute(*args).to_name
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# 1 = left; 2= left of left; 3 = left of left of left....
|
|
73
|
+
# @return [Cardname]
|
|
74
|
+
def nth_left_name n
|
|
75
|
+
(n >= length ? parts[0] : parts[0..-n - 1]).to_name
|
|
55
76
|
end
|
|
56
77
|
|
|
78
|
+
private
|
|
79
|
+
|
|
57
80
|
def parts_excluding *string
|
|
58
81
|
exclude_name = string.to_name
|
|
59
82
|
exclude_keys = exclude_name ? exclude_name.part_names.map(&:key) : []
|
|
60
83
|
parts_minus exclude_keys
|
|
61
84
|
end
|
|
62
85
|
|
|
86
|
+
def remove_context *from
|
|
87
|
+
return false unless from.compact.present?
|
|
88
|
+
|
|
89
|
+
remaining = parts_excluding(*from)
|
|
90
|
+
return false if remaining.compact.empty? || # all name parts in context
|
|
91
|
+
remaining == parts # no name parts in context
|
|
92
|
+
|
|
93
|
+
remaining
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# @return [Array <String>]
|
|
63
97
|
def parts_minus keys_to_ignore
|
|
64
98
|
parts.map do |part|
|
|
65
99
|
next if part.empty?
|
|
@@ -70,26 +104,6 @@ class Cardname
|
|
|
70
104
|
end
|
|
71
105
|
end
|
|
72
106
|
|
|
73
|
-
def absolute context
|
|
74
|
-
context = (context || "").to_name
|
|
75
|
-
new_parts = absolutize_contextual_parts context
|
|
76
|
-
return "" if new_parts.empty?
|
|
77
|
-
|
|
78
|
-
absolutize_extremes new_parts, context.s
|
|
79
|
-
new_parts.join self.class.joint
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def absolute_name *args
|
|
83
|
-
absolute(*args).to_name
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def nth_left n
|
|
87
|
-
# 1 = left; 2= left of left; 3 = left of left of left....
|
|
88
|
-
(n >= length ? parts[0] : parts[0..-n - 1]).to_name
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
private
|
|
92
|
-
|
|
93
107
|
def absolutize_contextual_parts context
|
|
94
108
|
parts.map do |part|
|
|
95
109
|
case part
|
|
@@ -118,7 +132,7 @@ class Cardname
|
|
|
118
132
|
def partmap_part match, context
|
|
119
133
|
l_s = match[1].size
|
|
120
134
|
r_s = !match[2].empty?
|
|
121
|
-
l_part = context.
|
|
135
|
+
l_part = context.nth_left_name l_s
|
|
122
136
|
r_s ? l_part.tag : l_part.s
|
|
123
137
|
end
|
|
124
138
|
|
data/lib/cardname/manipulate.rb
CHANGED
|
@@ -1,52 +1,47 @@
|
|
|
1
1
|
class Cardname
|
|
2
|
+
# methods for altering name
|
|
2
3
|
module Manipulate
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
def
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
return swap_part(old_name, new_name) if old_name.simple?
|
|
10
|
-
return self unless include? old_name
|
|
11
|
-
|
|
12
|
-
swap_all_subsequences(old_name, new_name).to_name
|
|
4
|
+
# alter cardname based on card index
|
|
5
|
+
# @return [Cardname]
|
|
6
|
+
def []= index, val
|
|
7
|
+
p = parts
|
|
8
|
+
p[index] = val
|
|
9
|
+
replace self.class.new(p)
|
|
13
10
|
end
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
newpart = newpart.to_name
|
|
20
|
-
|
|
21
|
-
parts.map do |p|
|
|
22
|
-
oldpart == p ? newpart : p
|
|
23
|
-
end.to_name
|
|
12
|
+
# append part to cardname
|
|
13
|
+
# @return [Cardname]
|
|
14
|
+
def << val
|
|
15
|
+
replace self.class.new(parts << val)
|
|
24
16
|
end
|
|
25
17
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return self unless starts_with_parts?(oldpiece)
|
|
32
|
-
return newpiece if oldpiece.num_parts == num_parts
|
|
33
|
-
|
|
34
|
-
self.class.new [newpiece, self[oldpiece.num_parts..-1]].flatten
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def num_parts
|
|
38
|
-
parts.length
|
|
39
|
-
end
|
|
18
|
+
# swap one name for another (keys are used for comparison)
|
|
19
|
+
# @return [Cardname]
|
|
20
|
+
def swap old, new
|
|
21
|
+
old_name = old.to_name
|
|
22
|
+
new_name = new.to_name
|
|
40
23
|
|
|
41
|
-
|
|
42
|
-
|
|
24
|
+
if old_name.num_parts > num_parts
|
|
25
|
+
self
|
|
26
|
+
elsif old_name.simple?
|
|
27
|
+
swap_part old_name, new_name
|
|
28
|
+
elsif include? old_name
|
|
29
|
+
swap_all_subsequences(old_name, new_name).to_name
|
|
30
|
+
else
|
|
31
|
+
self
|
|
32
|
+
end
|
|
43
33
|
end
|
|
44
34
|
|
|
35
|
+
# add a joint to name's beginning (if it doesn't already start with one)
|
|
36
|
+
# @return [String]
|
|
45
37
|
def prepend_joint
|
|
46
38
|
joint = self.class.joint
|
|
47
39
|
self =~ /^#{Regexp.escape joint}/ ? self : (joint + self)
|
|
48
40
|
end
|
|
41
|
+
alias_method :to_field, :prepend_joint
|
|
49
42
|
|
|
43
|
+
# substitute name, where it appears in str, with new string
|
|
44
|
+
# @return [String]
|
|
50
45
|
def sub_in str, with:
|
|
51
46
|
%i[capitalize downcase].product(%i[pluralize singularize])
|
|
52
47
|
.inject(str) do |s, (m1, m2)|
|
|
@@ -54,10 +49,12 @@ class Cardname
|
|
|
54
49
|
end
|
|
55
50
|
end
|
|
56
51
|
|
|
57
|
-
alias_method :to_field, :prepend_joint
|
|
58
|
-
|
|
59
52
|
private
|
|
60
53
|
|
|
54
|
+
def swap_part oldpart, newpart
|
|
55
|
+
parts.map { |p| oldpart == p ? newpart : p }.to_name
|
|
56
|
+
end
|
|
57
|
+
|
|
61
58
|
def swap_all_subsequences oldseq, newseq
|
|
62
59
|
res = []
|
|
63
60
|
i = 0
|
|
@@ -75,11 +72,5 @@ class Cardname
|
|
|
75
72
|
res += parts[i..-1] if i < num_parts
|
|
76
73
|
res
|
|
77
74
|
end
|
|
78
|
-
|
|
79
|
-
def ensure_simpleness part, msg=nil
|
|
80
|
-
return if part.to_name.simple?
|
|
81
|
-
|
|
82
|
-
raise StandardError, "'#{part}' has to be simple. #{msg}"
|
|
83
|
-
end
|
|
84
75
|
end
|
|
85
76
|
end
|
data/lib/cardname/parts.rb
CHANGED
|
@@ -1,82 +1,156 @@
|
|
|
1
1
|
class Cardname
|
|
2
2
|
# naming conventions:
|
|
3
|
-
#
|
|
4
|
-
#
|
|
3
|
+
#
|
|
4
|
+
# - methods that end with _name return {Cardname} objects
|
|
5
|
+
# - methods that end with _key return {Cardname#key case/space keys}
|
|
6
|
+
# - methods without _name or _key return Strings
|
|
7
|
+
#
|
|
5
8
|
module Parts
|
|
6
|
-
#
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
# the part of a compound name to the left of the rightmost joint.
|
|
10
|
+
# @example
|
|
11
|
+
# "A".cardname.left -> nil
|
|
12
|
+
# "A+B".cardname.left -> "A"
|
|
13
|
+
# "A+B+C".cardname.left -> "A+B"
|
|
14
|
+
# "A+B+C+D".cardname.left -> "A+B+C"
|
|
15
|
+
# @see #trunk
|
|
16
|
+
# @return [String]
|
|
17
|
+
def left
|
|
18
|
+
left_name&.s
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @see #left
|
|
22
|
+
# @return [String]
|
|
23
|
+
def left_key
|
|
24
|
+
left_name&.key
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @see #left
|
|
28
|
+
# @return [Cardname]
|
|
29
|
+
def left_name
|
|
30
|
+
simple? ? nil : self.class.new(part_names[0..-2])
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# for compound cards, an array of the left and right
|
|
34
|
+
# @example
|
|
35
|
+
# "A".cardname.parents -> []
|
|
36
|
+
# "A+B".cardname.parents -> ["A", "B"]
|
|
37
|
+
# "A+B+C".cardname.parents -> ["A+B", "C"]
|
|
38
|
+
# "A+B+C+D".cardname.parents -> ["A+B+C", "D"]
|
|
39
|
+
# @see #parts
|
|
40
|
+
# @return [Array <String>]
|
|
41
|
+
def parents
|
|
42
|
+
parent_names.map(&:s)
|
|
9
43
|
end
|
|
10
44
|
|
|
45
|
+
# @see #parents
|
|
46
|
+
# @return [Array <String>]
|
|
47
|
+
def parent_keys
|
|
48
|
+
parent_names.map(&:key)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @see #parents
|
|
52
|
+
# @return [Array <Cardname>]
|
|
53
|
+
def parent_names
|
|
54
|
+
simple? ? [] : [left_name, right_name]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# for compound cards, each joint separated part
|
|
58
|
+
# @example
|
|
59
|
+
# "A".cardname.parts -> []
|
|
60
|
+
# "A+B".cardname.parts -> ["A", "B"]
|
|
61
|
+
# "A+B+C".cardname.parts -> ["A", "B", "C"]
|
|
62
|
+
# "A+B+C+D".cardname.parts -> ["A", "B", C", "D"]
|
|
63
|
+
# @see #parents
|
|
64
|
+
# @return [Array <String>]
|
|
11
65
|
def parts
|
|
12
66
|
part_names.map(&:s)
|
|
13
67
|
end
|
|
14
68
|
alias_method :to_a, :parts
|
|
15
69
|
|
|
70
|
+
# @see #parts
|
|
71
|
+
# @return [Array <String>]
|
|
16
72
|
def part_keys
|
|
17
73
|
part_names.map(&:key)
|
|
18
74
|
end
|
|
19
75
|
|
|
20
|
-
|
|
21
|
-
|
|
76
|
+
# @see #parts
|
|
77
|
+
# @return [Array <Cardname>]
|
|
78
|
+
def part_names
|
|
79
|
+
@part_names ||= generate_part_names
|
|
22
80
|
end
|
|
23
81
|
|
|
82
|
+
# like #left, but returns self for simple cards
|
|
83
|
+
# @example
|
|
84
|
+
# "A".cardname.trunk -> "A"
|
|
85
|
+
# "A+B".cardname.trunk -> "A"
|
|
86
|
+
# "A+B+C".cardname.trunk -> "A+B"
|
|
87
|
+
# "A+B+C+D".cardname.trunk -> "A+B+C"
|
|
88
|
+
# @see #left
|
|
89
|
+
# @return [String]
|
|
24
90
|
def trunk
|
|
25
91
|
trunk_name.s
|
|
26
92
|
end
|
|
27
93
|
|
|
94
|
+
# @see #trunk
|
|
95
|
+
# @return [String]
|
|
28
96
|
def trunk_key
|
|
29
97
|
trunk_name.key
|
|
30
98
|
end
|
|
31
99
|
|
|
32
|
-
|
|
33
|
-
|
|
100
|
+
# @see #trunk
|
|
101
|
+
# @return [Cardname]
|
|
102
|
+
def trunk_name
|
|
103
|
+
simple? ? self : left_name
|
|
34
104
|
end
|
|
35
105
|
|
|
106
|
+
# like #right, but returns self for simple cards
|
|
107
|
+
# @see #right
|
|
108
|
+
# @example
|
|
109
|
+
# "A".cardname.tag -> "A"
|
|
110
|
+
# "A+B".cardname.tag -> "B"
|
|
111
|
+
# "A+B+C".cardname.tag -> "C"
|
|
112
|
+
# "A+B+C+D".cardname.tag -> "D"
|
|
113
|
+
# @return [String]
|
|
36
114
|
def tag
|
|
37
115
|
tag_name.s
|
|
38
116
|
end
|
|
39
117
|
|
|
118
|
+
# @see #tag
|
|
119
|
+
# @return [String]
|
|
40
120
|
def tag_key
|
|
41
121
|
tag_name.key
|
|
42
122
|
end
|
|
43
123
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def parents
|
|
49
|
-
parent_names.map(&:s)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def parent_keys
|
|
53
|
-
parent_names.map(&:key)
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def left_name
|
|
57
|
-
simple? ? nil : self.class.new(part_names[0..-2])
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def left
|
|
61
|
-
left_name&.s
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def left_key
|
|
65
|
-
left_name&.key
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def right_name
|
|
69
|
-
simple? ? nil : part_names[-1]
|
|
124
|
+
# @see #tag
|
|
125
|
+
# @return [Cardname]
|
|
126
|
+
def tag_name
|
|
127
|
+
simple? ? self : right_name
|
|
70
128
|
end
|
|
71
129
|
|
|
130
|
+
# the part of a compound name to the left of the rightmost joint.
|
|
131
|
+
#
|
|
132
|
+
# "A".cardname.right -> nil
|
|
133
|
+
# "A+B".cardname.right -> "B"
|
|
134
|
+
# "A+B+C".cardname.right -> "C"
|
|
135
|
+
# "A+B+C+D".cardname.right -> "D"
|
|
136
|
+
# @see #tag
|
|
137
|
+
# @return [String]
|
|
72
138
|
def right
|
|
73
139
|
right_name&.s
|
|
74
140
|
end
|
|
75
141
|
|
|
142
|
+
# @see #right
|
|
143
|
+
# @return [String]
|
|
76
144
|
def right_key
|
|
77
145
|
right_name&.key
|
|
78
146
|
end
|
|
79
147
|
|
|
148
|
+
# @see #right
|
|
149
|
+
# @return [Cardname]
|
|
150
|
+
def right_name
|
|
151
|
+
simple? ? nil : part_names[-1]
|
|
152
|
+
end
|
|
153
|
+
|
|
80
154
|
private
|
|
81
155
|
|
|
82
156
|
def generate_part_names
|
data/lib/cardname/pieces.rb
CHANGED
|
@@ -5,23 +5,35 @@ class Cardname
|
|
|
5
5
|
module Pieces
|
|
6
6
|
# self and all ancestors (= parts and recursive lefts)
|
|
7
7
|
# @example
|
|
8
|
-
# "A+B+C+D".to_name.pieces
|
|
9
|
-
#
|
|
8
|
+
# "A+B+C+D".to_name.pieces => ["A", "B", "C", "D", "A+B", "A+B+C", "A+B+C+D"]
|
|
9
|
+
# @return [Array <String>]
|
|
10
10
|
def pieces
|
|
11
|
-
simple? ? [self] : (parts +
|
|
11
|
+
simple? ? [self] : (parts + compound_pieces)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
# @see #pieces
|
|
15
|
+
# @return [Array <Cardname>]
|
|
14
16
|
def piece_names
|
|
15
17
|
pieces.map(&:to_name)
|
|
16
18
|
end
|
|
17
19
|
|
|
20
|
+
# parents, parents' parents, etc
|
|
21
|
+
# @example
|
|
22
|
+
# "A+B+C+D".to_name.ancestors => ["A", "B", "C", "D", "A+B", "A+B+C"]
|
|
23
|
+
# @return [Array <String>]
|
|
18
24
|
def ancestors
|
|
19
25
|
pieces.reject { |p| p == self }
|
|
20
26
|
end
|
|
21
27
|
|
|
28
|
+
# @see #ancestors
|
|
29
|
+
# @return [Array <Cardname>]
|
|
30
|
+
def ancestor_pieces
|
|
31
|
+
ancestors.map(&:to_name)
|
|
32
|
+
end
|
|
33
|
+
|
|
22
34
|
private
|
|
23
35
|
|
|
24
|
-
def
|
|
36
|
+
def compound_pieces
|
|
25
37
|
[].tap do |pieces|
|
|
26
38
|
parts[1..-1].inject parts[0] do |left, right|
|
|
27
39
|
piece = [left, right] * self.class.joint
|
data/lib/cardname/predicates.rb
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
class Cardname
|
|
2
|
+
# name methods returning true/false
|
|
2
3
|
module Predicates
|
|
3
|
-
# @return true if name has only one part
|
|
4
|
+
# @return [Boolean] true if name has only one part
|
|
4
5
|
def simple?
|
|
5
6
|
part_names if @simple.nil?
|
|
6
7
|
@simple
|
|
7
8
|
end
|
|
8
9
|
|
|
9
|
-
# @return true if name has more than one part
|
|
10
|
+
# @return [Boolean] true if name has more than one part
|
|
10
11
|
def compound?
|
|
11
12
|
!simple?
|
|
12
13
|
end
|
|
13
14
|
|
|
15
|
+
# @return [Boolean] true unless name contains banned characters
|
|
14
16
|
def valid?
|
|
15
17
|
return true if self.class.nothing_banned?
|
|
16
18
|
|
|
@@ -19,21 +21,21 @@ class Cardname
|
|
|
19
21
|
end
|
|
20
22
|
end
|
|
21
23
|
|
|
22
|
-
# @return true if name starts with the same parts as `prefix`
|
|
24
|
+
# @return [Boolean] true if name starts with the same parts as `prefix`
|
|
23
25
|
def starts_with_parts? *prefix
|
|
24
26
|
start_name = prefix.to_name
|
|
25
27
|
start_name == self[0, start_name.num_parts]
|
|
26
28
|
end
|
|
27
29
|
alias_method :start_with_parts?, :starts_with_parts?
|
|
28
30
|
|
|
29
|
-
# @return true if name ends with the same parts as `prefix`
|
|
31
|
+
# @return [Boolean] true if name ends with the same parts as `prefix`
|
|
30
32
|
def ends_with_parts? *suffix
|
|
31
33
|
end_name = suffix.to_name
|
|
32
34
|
end_name == self[-end_name.num_parts..-1]
|
|
33
35
|
end
|
|
34
36
|
alias_method :end_with_parts?, :ends_with_parts?
|
|
35
37
|
|
|
36
|
-
# @return true if name has a chain of parts that equals `subname`
|
|
38
|
+
# @return [Boolean] true if name has a chain of parts that equals `subname`
|
|
37
39
|
def include? subname
|
|
38
40
|
subkey = subname.to_name.key
|
|
39
41
|
key =~ /(^|#{JOINT_RE})#{Regexp.quote subkey}($|#{JOINT_RE})/
|
data/lib/cardname/variants.rb
CHANGED
|
@@ -2,6 +2,7 @@ require "htmlentities"
|
|
|
2
2
|
|
|
3
3
|
class Cardname
|
|
4
4
|
module Variants
|
|
5
|
+
# @return [String]
|
|
5
6
|
def simple_key
|
|
6
7
|
return "" if empty?
|
|
7
8
|
|
|
@@ -14,6 +15,8 @@ class Cardname
|
|
|
14
15
|
.join("_")
|
|
15
16
|
end
|
|
16
17
|
|
|
18
|
+
# URI-friendly version of name. retains case, underscores for space
|
|
19
|
+
# @return [String]
|
|
17
20
|
def url_key
|
|
18
21
|
part_names.map do |part_name|
|
|
19
22
|
stripped = part_name.decoded.gsub(/[^#{OK4KEY_RE}]+/, " ").strip
|
|
@@ -24,18 +27,16 @@ class Cardname
|
|
|
24
27
|
# safe to be used in HTML as id ('*' and '+' are not allowed),
|
|
25
28
|
# but the key is no longer unique.
|
|
26
29
|
# For example "A-XB" and "A+*B" have the same safe_key
|
|
30
|
+
# @return [String]
|
|
27
31
|
def safe_key
|
|
28
32
|
key.tr("*", "X").tr self.class.joint, "-"
|
|
29
33
|
end
|
|
30
34
|
|
|
35
|
+
# @return [String]
|
|
31
36
|
def decoded
|
|
32
37
|
@decoded ||= s.index("&") ? HTMLEntities.new.decode(s) : s
|
|
33
38
|
end
|
|
34
39
|
|
|
35
|
-
def to_sym
|
|
36
|
-
s.to_sym
|
|
37
|
-
end
|
|
38
|
-
|
|
39
40
|
private
|
|
40
41
|
|
|
41
42
|
def uninflect_method
|
data/lib/cardname.rb
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
require "active_support/inflector"
|
|
4
4
|
require "htmlentities"
|
|
5
5
|
|
|
6
|
+
# The Cardname class generalizes the core naming concepts of Decko/Card. The most central
|
|
7
|
+
# of these is the idea that compound names can be formed by combining simple names.
|
|
8
|
+
#
|
|
9
|
+
#
|
|
6
10
|
class Cardname < String
|
|
7
11
|
require "cardname/parts"
|
|
8
12
|
require "cardname/pieces"
|
|
@@ -11,6 +15,7 @@ class Cardname < String
|
|
|
11
15
|
require "cardname/predicates"
|
|
12
16
|
require "cardname/manipulate"
|
|
13
17
|
require "cardname/fields"
|
|
18
|
+
require "cardname/class_methods"
|
|
14
19
|
|
|
15
20
|
include Parts
|
|
16
21
|
include Pieces
|
|
@@ -19,8 +24,7 @@ class Cardname < String
|
|
|
19
24
|
include Predicates
|
|
20
25
|
include Manipulate
|
|
21
26
|
include Fields
|
|
22
|
-
|
|
23
|
-
OK4KEY_RE = '\p{Word}\*'
|
|
27
|
+
extend ClassMethods
|
|
24
28
|
|
|
25
29
|
cattr_accessor :joint, :banned_array, :var_re, :uninflect, :params, :session, :stabilize
|
|
26
30
|
|
|
@@ -30,49 +34,9 @@ class Cardname < String
|
|
|
30
34
|
self.uninflect = :singularize
|
|
31
35
|
self.stabilize = false
|
|
32
36
|
|
|
37
|
+
OK4KEY_RE = '\p{Word}\*'
|
|
33
38
|
JOINT_RE = Regexp.escape joint
|
|
34
39
|
|
|
35
|
-
class << self
|
|
36
|
-
def new obj
|
|
37
|
-
return obj if obj.is_a? self.class
|
|
38
|
-
|
|
39
|
-
str = stringify(obj)
|
|
40
|
-
cache[str] ||= super(str)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def reset
|
|
44
|
-
@cache = {}
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def nothing_banned?
|
|
48
|
-
return @nothing_banned unless @nothing_banned.nil?
|
|
49
|
-
|
|
50
|
-
@nothing_banned = banned_array.empty?
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def banned_re
|
|
54
|
-
@banned_re ||= /[#{Regexp.escape((banned_array + [joint])).join}]/
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def split_parts str
|
|
58
|
-
str.split(/\s*#{JOINT_RE}\s*/, -1)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def cache
|
|
62
|
-
@cache ||= {}
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
private
|
|
66
|
-
|
|
67
|
-
def stringify obj
|
|
68
|
-
if obj.is_a?(Array)
|
|
69
|
-
obj.map(&:to_s) * joint
|
|
70
|
-
else
|
|
71
|
-
obj.to_s
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
40
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
77
41
|
# ~~~~~~~~~~~~~~~~~~~~~~ INSTANCE ~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
78
42
|
attr_reader :key
|
|
@@ -87,31 +51,32 @@ class Cardname < String
|
|
|
87
51
|
freeze
|
|
88
52
|
end
|
|
89
53
|
|
|
54
|
+
# simple string version of name
|
|
55
|
+
# @return [String]
|
|
90
56
|
def s
|
|
91
57
|
String.new self
|
|
92
58
|
end
|
|
93
59
|
alias_method :to_s, :s
|
|
94
60
|
alias_method :to_str, :s
|
|
95
|
-
# alias_method :dup, :clone
|
|
96
61
|
|
|
62
|
+
# @return [Cardname]
|
|
97
63
|
def to_name
|
|
98
64
|
self
|
|
99
65
|
end
|
|
100
66
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
replace self.class.new(p)
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
def << val
|
|
108
|
-
replace self.class.new(parts << val)
|
|
67
|
+
# @return [Symbol]
|
|
68
|
+
def to_sym
|
|
69
|
+
s.to_sym
|
|
109
70
|
end
|
|
110
71
|
|
|
72
|
+
# the key defines the namespace
|
|
73
|
+
# @return [String]
|
|
111
74
|
def key
|
|
112
75
|
@key ||= generate_key.freeze
|
|
113
76
|
end
|
|
114
77
|
|
|
78
|
+
# test for same key
|
|
79
|
+
# @return [Boolean]
|
|
115
80
|
def == other
|
|
116
81
|
key ==
|
|
117
82
|
case
|
|
@@ -121,6 +86,18 @@ class Cardname < String
|
|
|
121
86
|
end
|
|
122
87
|
end
|
|
123
88
|
|
|
89
|
+
# cardname based on part index
|
|
90
|
+
# @return [Cardname]
|
|
91
|
+
def [] *args
|
|
92
|
+
self.class.new part_names[*args]
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# @see #parts
|
|
96
|
+
# @return [Integer]
|
|
97
|
+
def num_parts
|
|
98
|
+
parts.length
|
|
99
|
+
end
|
|
100
|
+
|
|
124
101
|
private
|
|
125
102
|
|
|
126
103
|
def generate_key
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cardname
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.15.
|
|
4
|
+
version: 0.15.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ethan McCutchen
|
|
@@ -10,7 +10,7 @@ authors:
|
|
|
10
10
|
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date: 2023-
|
|
13
|
+
date: 2023-03-29 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: activesupport
|
|
@@ -50,6 +50,7 @@ files:
|
|
|
50
50
|
- README.md
|
|
51
51
|
- Rakefile
|
|
52
52
|
- lib/cardname.rb
|
|
53
|
+
- lib/cardname/class_methods.rb
|
|
53
54
|
- lib/cardname/contextual.rb
|
|
54
55
|
- lib/cardname/fields.rb
|
|
55
56
|
- lib/cardname/manipulate.rb
|