richtext 0.1.0 → 0.2.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: a41039f175ce7ee67d507040de2a1c84f58c9069
4
- data.tar.gz: 60ae51774de4953ea1b5e59ef3ffb8afd2f520ac
3
+ metadata.gz: 1056b27569ae91520079931c9a5da0efc921467a
4
+ data.tar.gz: 7a58d34765134f7b85a3254de3e09a1c59cada8e
5
5
  SHA512:
6
- metadata.gz: 86924ff26a091d4087245467c47e43aabd5499eb8fc785ac2ba8b2dbca596ab49d9ba79399ad6c51e65c6f629fb9ba38e6d02cd8725845f9a6c1d0ffe84f70a6
7
- data.tar.gz: 8e08c49fcb57d006235eaa58258b9e0dc0b0e0e5a753594b15becda8b7715413ce6a9b0ef5f5bea8be0a1075e05902d6dad5194e87635b9d34a5dd51432dac42
6
+ metadata.gz: 64eac480d4354c710fc26e97888314b9e5143ea56e3ad4443530712d63168470fff018f71f768d5da6402892e58f7efd334fe07903042d78e1e605008ee25daa
7
+ data.tar.gz: 56c7742bfdeaac68dba853766e0214f98bbd6ab0436d1ba188a9a98ff151783d218885bcf28cb1852c21d7ce0fb429b6ab97c83eacb83ece8ab85ebee2c04df1
data/README.md CHANGED
@@ -35,7 +35,7 @@ entry = rt.append('world', bold: true, my_attribute: '.')
35
35
  # Some common styling attributes are supported directly
36
36
  # This line is equivalent to entry[:italic] = true
37
37
  entry.italic = true
38
- # Under the covers the attributes are stored as
38
+ # Under the covers the attributes are stored as
39
39
  # key-value pairs, so any attribute is valid
40
40
  entry[:my_attribute] = '!'
41
41
 
@@ -43,12 +43,12 @@ entry[:my_attribute] = '!'
43
43
  puts rt.to_s # => 'hello world'
44
44
 
45
45
  # Or style the text yourself
46
- html = rt.to_s do |entry, string|
46
+ html = rt.to_s do |e, string|
47
47
  # Access the attributes from the entry and format the
48
48
  # string accordingly
49
- string += entry[:my_attribute] if entry[:my_attribute]
50
- string = "<b>#{string}</b>" if entry.bold?
51
-
49
+ string += e[:my_attribute] if e[:my_attribute]
50
+ string = "<b>#{string}</b>" if e.bold?
51
+
52
52
  # Return the formatted string at the end of the block
53
53
  string
54
54
  end
@@ -60,30 +60,27 @@ Implementing new formats is easy. Just extend the `RichText::Document` class and
60
60
 
61
61
  ```ruby
62
62
  class MyFormat < RichText::Document
63
- # Use this method to signal if the document needs to be
64
- # parsed, or if its raw form will work.
65
63
  def should_parse?
66
64
  true
67
65
  end
68
66
 
69
- def self.parse string
70
- base = RichText::Document::Entry.new
67
+ def self.parse(base, string)
71
68
  # Format specific implementation to parse a string. Here
72
69
  # each word is represented by its own entry. Entries are
73
70
  # given a random visibility attribute.
74
71
  string.split(' ').each do |word|
75
- entry = RichText::Document::Entry.new word, visible: (word.length > 6)
76
- base.add entry
72
+ base.create_child word, visible: (word.length > 6)
77
73
  end
78
- base
79
74
  end
80
75
 
81
- def self.render base
76
+ def self.render(base)
82
77
  # Format specific implementation to render the document
83
- base.to_s do |entry, string|
78
+ str = base.to_s do |entry, string|
84
79
  next string unless entry.leaf?
85
80
  entry[:visible] ? string + ' ' : ''
86
- end.rstrip!
81
+ end
82
+
83
+ str.rstrip
87
84
  end
88
85
  end
89
86
 
@@ -1,142 +1,126 @@
1
- # Entry
2
- #
3
- # The Entry class extends the basic Node class and adds methods that make
4
- # handling text a little nicer. Essentially the :text attribute is given special
5
- # status by allowing it to a) be set during initialization, b) only visible in
6
- # leaf nodes and c) copied over when adding children to leaf nodes.
7
- #
8
- # Some attributes are also supported explicitly by the inclusion of special
9
- # accesser methods. The attributes are are bold, italic, underline, color and
10
- # font.
11
-
12
1
  module RichText
13
2
  class Document
3
+ # Entry
4
+ #
5
+ # The Entry class extends the basic Node class and adds methods that make
6
+ # handling text a little nicer. Essentially the :text attribute is given
7
+ # special status by allowing it to a) be set during initialization, b) only
8
+ # visible in leaf nodes and c) copied over when adding children to leaf
9
+ # nodes.
10
+ #
11
+ # Some attributes are also supported explicitly by the inclusion of special
12
+ # accesser methods. The attributes are are bold, italic, underline, color
13
+ # and font.
14
+ #
14
15
  class Entry < Node
15
-
16
16
  # Initialize
17
17
  #
18
- # Extend the default Node initializer by also accepting a string. It will,
18
+ # Extend the default Node initializer by also accepting a string. It will,
19
19
  # if given, be stored as a text attribute.
20
-
21
- def initialize text = nil, **attributes
20
+ def initialize(text = nil, **attributes)
22
21
  super attributes
23
22
  self[:text] = text if text
24
23
  end
25
-
26
-
24
+
27
25
  # Text
28
26
  #
29
- # Read the text of the node. This will return nil unless the node is a
30
- # leaf node. Note that nodes that are not leafs can have the text entry,
27
+ # Read the text of the node. This will return nil unless the node is a
28
+ # leaf node. Note that nodes that are not leafs can have the text entry,
31
29
  # but it is discouraged by dissalowing access using this method.
32
-
33
30
  def text
34
- if leaf?
35
- self[:text] || ''
36
- else
37
- nil
38
- end
31
+ self[:text] || '' if leaf?
39
32
  end
40
-
41
-
42
- # Add child
33
+
34
+ # Append
43
35
  #
44
- # A child is either another node or any object that respond to #to_s.
45
-
46
- def add *new_children
36
+ # Since the text attribute is treated differently, and only leaf nodes can
37
+ # expose it, it must be pushed to a new child if a) this node was a leaf
38
+ # prior to this method call and b) its text attribute is not empty.
39
+ def <<(child)
47
40
  if leaf?
48
- # Remove the text entry from the node and put it in a new leaf node
41
+ # Remove the text entry from the node and put it in a new leaf node
49
42
  # among the children, unless it is empty
50
- if t = @attributes.delete(:text)
51
- new_children.unshift self.class.new(t) unless t.empty?
43
+ if (t = @attributes.delete :text)
44
+ create_child(t) unless t.empty?
52
45
  end
53
46
  end
54
-
47
+
55
48
  super
56
49
  end
57
-
58
- alias_method :<<, :add
59
-
60
-
50
+
51
+ def create_child(text = '', **attributes)
52
+ super attributes.merge(text: text)
53
+ end
54
+
61
55
  # To String
62
56
  #
63
- # Combine the text from all the leaf nodes in the tree, from left to
64
- # right. If a block is given the node, along with its text will be passed
65
- # as arguments. The block will be called recursivly, starting at the leaf
66
- # nodes and propagating up until the entire tree has been "rendered" in
57
+ # Combine the text from all the leaf nodes in the tree, from left to
58
+ # right. If a block is given the node, along with its text will be passed
59
+ # as arguments. The block will be called recursivly, starting at the leaf
60
+ # nodes and propagating up until the entire tree has been "rendered" in
67
61
  # this way.
68
-
69
- def to_s &block
70
- string = leaf? ?
71
- text :
72
- @children.reduce('') {|str, child| str + child.to_s(&block) }
73
-
62
+ def to_s(&block)
63
+ string =
64
+ if leaf?
65
+ text
66
+ else
67
+ @children.reduce('') { |a, e| a + e.to_s(&block) }
68
+ end
69
+
74
70
  block_given? ? yield(self, string) : string
75
71
  end
76
-
77
-
72
+
78
73
  # Supported Text Attributes
79
- #
80
-
81
-
74
+
82
75
  # Bold
83
76
  #
84
-
85
77
  def bold?
86
78
  self[:bold]
87
79
  end
88
-
89
- def bold= b
80
+
81
+ def bold=(b)
90
82
  self[:bold] = b ? true : false
91
83
  end
92
-
93
-
84
+
94
85
  # Italic
95
86
  #
96
-
97
87
  def italic?
98
88
  self[:italic]
99
89
  end
100
-
101
- def italic= i
90
+
91
+ def italic=(i)
102
92
  self[:italic] = i ? true : false
103
93
  end
104
-
105
-
94
+
106
95
  # Underline
107
96
  #
108
-
109
97
  def underline?
110
98
  self[:underline]
111
99
  end
112
-
113
- def underline= u
100
+
101
+ def underline=(u)
114
102
  self[:underline] = u ? true : false
115
103
  end
116
-
117
-
104
+
118
105
  # Color
119
106
  #
120
-
121
107
  def color
122
108
  self[:color]
123
109
  end
124
-
125
- def color= c
110
+
111
+ def color=(c)
126
112
  self[:color] = c
127
113
  end
128
-
129
-
114
+
130
115
  # Font
131
116
  #
132
-
133
117
  def font
134
118
  self[:font]
135
119
  end
136
-
137
- def font= f
120
+
121
+ def font=(f)
138
122
  self[:font] = f
139
123
  end
140
124
  end
141
125
  end
142
- end
126
+ end
@@ -1,45 +1,46 @@
1
1
  module RichText
2
+ # Document
3
+ #
2
4
  class Document
5
+ attr_reader :raw
6
+ protected :raw
7
+
3
8
  # Initialize
4
9
  #
5
- # Create a new RichText Document, either from a string or from an existing
6
- # ducument. That feature is particularly useful when converting between
10
+ # Create a new RichText Document, either from a string or from an existing
11
+ # ducument. That feature is particularly useful when converting between
7
12
  # formats.
8
13
  #
9
- # When given a string or a RichText Document of the same class no parsing is
10
- # performed. Only when given a document of a different subclass will the
11
- # parser need to be run parsed. Note that the document(s) may already be in
12
- # parsed form, in which case no further parsing is performed. See #base for
14
+ # When given a string or a RichText Document of the same class no parsing is
15
+ # performed. Only when given a document of a different subclass will the
16
+ # parser need to be run parsed. Note that the document(s) may already be in
17
+ # parsed form, in which case no further parsing is performed. See #base for
13
18
  # more details.
14
-
15
- def initialize arg = ''
16
- @base, @raw = if self.class == arg.class
17
- arg.parsed? ?
18
- [arg.base, nil] :
19
- [nil, arg.raw]
20
- elsif Document === arg
21
- # For any other RichText object we take the base node
22
- [arg.base, nil]
23
- elsif Entry === arg
24
- # Also accept an Entry which will be used as the
25
- # document base
26
- [arg, nil]
27
- else
28
- [nil, arg.to_s]
29
- end
19
+ def initialize(arg = '')
20
+ @base, @raw =
21
+ if arg.class == self.class
22
+ arg.parsed? ? [arg.base, nil] : [nil, arg.raw]
23
+ elsif arg.is_a? Document
24
+ # For any other RichText object we take the base node
25
+ [arg.base, nil]
26
+ elsif arg.is_a? Entry
27
+ # Also accept an Entry which will be used as the
28
+ # document base
29
+ [arg, nil]
30
+ else
31
+ [nil, arg.to_s]
32
+ end
30
33
  end
31
-
32
-
34
+
33
35
  # To String
34
36
  #
35
37
  # Use the static implementation of .render to convert the document back into
36
- # a string. If the document was never parsed (and is unchanged) the
38
+ # a string. If the document was never parsed (and is unchanged) the
37
39
  # origninal string is just returned.
38
40
  #
39
- # If a block is given it will be used in place of .render to format the node
41
+ # If a block is given it will be used in place of .render to format the node
40
42
  # tree.
41
-
42
- def to_s &block
43
+ def to_s(&block)
43
44
  if block_given?
44
45
  base.to_s(&block)
45
46
  elsif parsed? || should_parse?
@@ -48,133 +49,115 @@ module RichText
48
49
  @raw
49
50
  end
50
51
  end
51
-
52
-
52
+
53
53
  # Add (+)
54
54
  #
55
- # Add this RichText to another.
56
-
57
- def + other
55
+ # Add another Document to this one. If the two are of (exactly) the same
56
+ # class and neither one has been parsed, the two raw strings will be
57
+ # concatenated. If the other is a Document the two base nodes will be merged
58
+ # and the new root added to a new Document.
59
+ #
60
+ # Lastly, if other is a string it will first be wraped in a new Document and
61
+ # then added to this one.
62
+ def +(other)
58
63
  # If the other object is of the same class, and neither
59
64
  # one of the texts have been parsed, we can concatenate
60
65
  # the raw inputs together
61
66
  if other.class == self.class && !parsed? && !other.parsed?
62
- return self.class.new (@raw + other.raw)
67
+ return self.class.new(@raw + other.raw)
63
68
  end
64
-
69
+
65
70
  # Same root class
66
- if Document === other
67
- return self.class.new (base + other.base)
68
- end
69
-
70
- unless other.respond_to?(:to_s)
71
+ return self.class.new(base + other.base) if other.is_a? Document
72
+
73
+ unless other.respond_to? :to_s
71
74
  raise TypeError,
72
- "cannot add #{other.class.name} to #{self.class.name}"
75
+ "Cannot add #{other.class.name} to #{self.class.name}"
73
76
  end
74
-
77
+
75
78
  # Assume that the input is a raw string of the same
76
79
  # class as the current RichText object and wrap it
77
80
  # before adding it
78
81
  self + self.class.new(other)
79
82
  end
80
-
81
-
82
- def append string, **attributes
83
- node = Entry.new(string, **attributes)
84
- base.add node
85
- node
83
+
84
+ # Append
85
+ #
86
+ #
87
+ def append(string, **attributes)
88
+ base.create_child string, **attributes
86
89
  end
87
-
88
-
90
+
89
91
  # Base
90
92
  #
91
- # Getter for the base node. If the raw input has not yet been
93
+ # Getter for the base node. If the raw input has not yet been
92
94
  # parsed that will happen first, before the base node is returned.
93
-
94
95
  def base
95
96
  unless @base
96
97
  @base = Entry.new
97
98
  self.class.parse @base, @raw
98
- @raw = nil # Free the cached string
99
+ @raw = nil
99
100
  end
100
-
101
+
101
102
  @base
102
103
  end
103
-
104
- alias_method :root, :base
105
-
106
-
107
- # Raw
108
- #
109
- # Protected getter for the raw input.
110
-
111
- protected def raw
112
- @raw
113
- end
114
-
115
-
104
+
105
+ alias root base
106
+
116
107
  # Parsed?
117
108
  #
118
- # Returns true if the raw input has been parsed and the internal
109
+ # Returns true if the raw input has been parsed and the internal
119
110
  # representation is now a tree of nodes.
120
-
121
111
  def parsed?
122
112
  @raw.nil?
123
113
  end
124
-
125
-
114
+
126
115
  protected def should_parse?
127
116
  false
128
117
  end
129
-
130
-
118
+
131
119
  # Each Node
132
120
  #
133
121
  # Iterate over all Entry nodes in the document tree.
134
-
135
- def each_node &block
122
+ def each_node(&block)
136
123
  base.each(&block)
137
124
  end
138
-
139
- alias_method :each_entry, :each_node
140
-
141
-
125
+
126
+ alias each_entry each_node
127
+
142
128
  # Parse
143
129
  #
144
- # Document type specific method for parsing a string and turning it into a
145
- # tree of entry nodes. This method is intended to be overridden when the
146
- # Document is subclassed. The default implementation just creates a top
130
+ # Document type specific method for parsing a string and turning it into a
131
+ # tree of entry nodes. This method is intended to be overridden when the
132
+ # Document is subclassed. The default implementation just creates a top
147
133
  # level Entry containing the given string.
148
-
149
- def self.parse base, string
134
+
135
+ def self.parse(base, string)
150
136
  base[:text] = string
151
137
  end
152
-
153
-
138
+
154
139
  # Render
155
140
  #
156
- # Document type specific method for rendering a tree of entry nodes. This
157
- # method is intended to be overridden when the Document is subclassed. The
141
+ # Document type specific method for rendering a tree of entry nodes. This
142
+ # method is intended to be overridden when the Document is subclassed. The
158
143
  # default implementation just concatenates the text entries into.
159
-
160
- def self.render base
144
+
145
+ def self.render(base)
161
146
  base.to_s
162
147
  end
163
-
164
-
148
+
165
149
  # From
166
150
  #
167
- # Convenience method for instansiating one RichText object from another. The
168
- # methods only purpose is to make that intent more clear, and to make the
151
+ # Convenience method for instansiating one RichText object from another. The
152
+ # methods only purpose is to make that intent more clear, and to make the
169
153
  # creation from another RichText object explicit.
170
-
171
- def self.from doc
172
- unless Document === doc
173
- raise TypeError,
174
- "Can only create a #{self.name} from other RichText objects"
154
+ def self.from(doc)
155
+ unless doc.is_a? Document
156
+ raise TypeError,
157
+ "Can only create a #{name} from other RichText Documents"
175
158
  end
176
-
177
- self.new doc
159
+
160
+ new doc
178
161
  end
179
162
  end
180
- end
163
+ end
data/lib/richtext/node.rb CHANGED
@@ -1,114 +1,105 @@
1
- # Node
2
- #
3
- # A Node can have children, which themselvs can have children. A tree like
4
- # structure can thus be formed by composing multiple Nodes. An example of such a
5
- # tree structure can be seen below.
6
- #
7
- # The Node class implements some convenience methods for iterating, left to
8
- # right, over either all
9
- # - nodes in the tree
10
- # - leafs in the tree
11
- # - direct decendant of a node
12
- #
13
- # In addition to having children a Node can also have attributes, represented by # simple key => value pairs.
14
- #
15
- # Example Tree
16
- # +--------------------------+
17
- # A <- Root Node | Left to right order: ABC |
18
- # / \ +--------------------------+
19
- # Leaf Node -> B C <- Child to A
20
- # (no children) /|\
21
- # ...
22
- #
23
-
24
1
  module RichText
2
+ # Node
3
+ #
4
+ # A Node can have children, which themselvs can have children. A tree like
5
+ # structure can thus be formed by composing multiple Nodes. An example of such
6
+ # a tree structure can be seen below.
7
+ #
8
+ # The Node class implements some convenience methods for iterating, left to
9
+ # right, over either all
10
+ # - nodes in the tree
11
+ # - leafs in the tree
12
+ # - direct decendant of a node
13
+ #
14
+ # In addition to having children a Node can also have attributes, represented
15
+ # by simple key => value pairs.
16
+ #
17
+ # Example Tree
18
+ # +--------------------------+
19
+ # A <- Root Node | Left to right order: ABC |
20
+ # / \ +--------------------------+
21
+ # Leaf Node -> B C <- Child to A
22
+ # (no children) /|\
23
+ # ...
24
+ #
25
25
  class Node
26
26
  include Enumerable
27
-
28
- attr_reader :attributes
29
-
30
- def initialize **attributes
27
+
28
+ attr_reader :attributes, :children
29
+ protected :children
30
+
31
+ def initialize(**attributes)
31
32
  @children = []
32
33
  @attributes = attributes
33
- #@attributes[:text] = text if text
34
34
  end
35
-
36
-
37
- def initialize_copy original
35
+
36
+ def initialize_copy(original)
38
37
  @children = original.children.map(&:dup)
39
38
  @attributes = original.attributes.dup
40
39
  end
41
-
42
-
40
+
43
41
  # Leaf?
44
42
  #
45
43
  # Returns true if this node a leaf (childless) node.
46
-
47
44
  def leaf?
48
45
  @children.empty?
49
46
  end
50
-
51
-
52
- # Children
53
- #
54
- # Protected accessor for the children array. This array should never be
55
- # mutated from the outside and is only protected rather than private to be
56
- # accessable to ther Nodes.
57
-
58
- protected def children
59
- @children
60
- end
61
-
62
-
63
- # Add child
47
+
48
+ # Append
64
49
  #
65
- # A child is either another node or any object that respond to #to_s.
66
-
67
- def add *new_children
68
- new_children.each do |c|
69
- @children << ((Node === c) ? c : self.class.new(c))
50
+ # Add a child to the end of the node child list. The child must be of this
51
+ # class to be accepted. Note that subclasses of Node will not accept regular
52
+ # Nodes. The method returns self so that multiple children can be added via
53
+ # chaining:
54
+ # root << child_a << child_b
55
+ def <<(child)
56
+ unless child.is_a? self.class
57
+ raise TypeError,
58
+ "Only objects of class #{self.class.name} can be appended"
70
59
  end
71
60
 
72
- @children
61
+ @children << child
62
+ self
73
63
  end
74
-
75
- alias_method :<<, :add
76
-
77
-
64
+
65
+ # Create Child
66
+ #
67
+ # Create and append a new child, initialized with the given attributes.
68
+ def create_child(**attributes)
69
+ child = self.class.new(**attributes)
70
+ self << child
71
+ child
72
+ end
73
+
78
74
  # Add (+)
79
75
  #
80
76
  # Combines two nodes by creating a new root and adding the two as children.
81
-
82
- def + other
83
- self.class.new.tap {|root| root.add self, other }
77
+ def +(other)
78
+ self.class.new.tap { |root| root << self << other }
84
79
  end
85
-
86
-
80
+
87
81
  # Each
88
82
  #
89
83
  # Iterate over each node in the tree, including self.
90
-
91
- def each &block
84
+ def each(&block)
92
85
  return to_enum(__callee__) unless block_given?
93
-
86
+
94
87
  yield self
95
-
88
+
96
89
  @children.each do |child|
97
90
  yield child
98
91
  child.each(&block) unless child.leaf?
99
92
  end
100
93
  end
101
-
102
-
94
+
103
95
  # Each Leaf
104
96
  #
105
- # Iterate over each leaf in the tree. This method will yield the leaf nodes
97
+ # Iterate over each leaf in the tree. This method will yield the leaf nodes
106
98
  # of the tree from left to right.
107
-
108
- def each_leaf &block
99
+ def each_leaf(&block)
109
100
  return to_enum(__callee__) unless block_given?
110
101
  return yield self if leaf?
111
-
102
+
112
103
  @children.each do |child|
113
104
  if child.leaf?
114
105
  yield child
@@ -117,72 +108,60 @@ module RichText
117
108
  end
118
109
  end
119
110
  end
120
-
121
-
111
+
122
112
  # Each child
123
113
  #
124
114
  # Iterate over the children of this node.
125
-
126
- def each_child &block
115
+ def each_child(&block)
127
116
  @children.each(&block)
128
117
  end
129
-
130
-
118
+
131
119
  # Attribute accessor
132
120
  #
133
- # Read and write an attribute of the node. Attributes are simply key-value
121
+ # Read and write an attribute of the node. Attributes are simply key-value
134
122
  # pairs stored internally in a hash.
135
-
136
- def [] attribute
123
+ def [](attribute)
137
124
  @attributes[attribute]
138
125
  end
139
-
140
- def []= attribute, value
126
+
127
+ def []=(attribute, value)
141
128
  @attributes[attribute] = value
142
129
  end
143
-
144
-
130
+
145
131
  # Count
146
132
  #
147
133
  # Returns the child count of this node.
148
-
149
134
  def count
150
135
  @children.size
151
136
  end
152
-
153
-
137
+
154
138
  # Size
155
139
  #
156
140
  # Returns the size of the tree where this node is the root.
157
-
158
141
  def size
159
- @children.reduce(1) {|total, child| total + child.size }
142
+ @children.reduce(1) { |a, e| a + e.size }
160
143
  end
161
-
162
-
144
+
163
145
  # Minimal?
164
146
  #
165
- # Test if the tree under this node is minimal or not. A non minimal tree
147
+ # Test if the tree under this node is minimal or not. A non minimal tree
166
148
  # contains children which themselvs only have one child.
167
-
168
149
  def minimal?
169
- all? {|node| node.count != 1 }
150
+ all? { |node| node.count != 1 }
170
151
  end
171
-
172
-
152
+
173
153
  # Optimize!
174
154
  #
175
- # Go through each child and merge any node that a) is not a lead node and b)
176
- # only has one child, with its child. The attributes of the child will
155
+ # Go through each child and merge any node that a) is not a lead node and b)
156
+ # only has one child, with its child. The attributes of the child will
177
157
  # override those of the parent.
178
-
179
158
  def optimize!
180
159
  # If the node is a leaf it cannot be optimized further
181
160
  return if leaf?
182
-
161
+
183
162
  # First optimize each of the children
184
163
  @children.map(&:optimize!)
185
-
164
+
186
165
  # If we only have one child it is superfluous and
187
166
  # should be merged. That means this node will inherrit
188
167
  # the children of the single child as well as its
@@ -194,41 +173,40 @@ module RichText
194
173
  @attributes.merge! child.attributes
195
174
  end
196
175
  end
197
-
198
-
176
+
177
+ def optimize
178
+ dup.optimize!
179
+ end
180
+
199
181
  # Shallow equality (exclude children)
200
182
  #
201
183
  # Returns true if the other node has the exact same attributes.
202
-
203
- def === other
204
- @attributes == other.attributes
184
+ def equal?(other)
185
+ count == other.count && @attributes == other.attributes
205
186
  end
206
-
207
-
187
+
208
188
  # Deep equality (include children)
209
189
  #
210
- # Returns true if the other node has the same attributes and its children
190
+ # Returns true if the other node has the same attributes and its children
211
191
  # are also identical.
212
-
213
- def == other
192
+ def ==(other)
214
193
  # First make sure the nodes child count matches
215
- return false unless count == other.count
216
-
217
- # The nodes are not equal if their attributes do not
218
- # match
219
- return false unless self === other
220
-
194
+ return false unless equal? other
195
+
221
196
  # Lastly make sure all of the children are equal
222
- each_child.zip(other.each_child).all? {|c| c[0] == c[1] }
197
+ each_child.zip(other.each_child).all? { |c| c[0] == c[1] }
223
198
  end
224
-
225
-
199
+
226
200
  def inspect
227
- children = @children.reduce(''){|s, c|
228
- s + "\n" + c.inspect.gsub(/(^)/) { $1 + ' ' }}
229
-
230
- "#<%{name} %<a>p:%<id>#x>%{children}" % {
231
- name: self.class.name, id: self.object_id, a: @attributes, children: children}
201
+ children = @children.reduce('') do |s, c|
202
+ s + "\n" + c.inspect.gsub(/(^)/) { |m| m[1] + ' ' }
203
+ end
204
+
205
+ format '#<%{name} %<attrs>p:%<id>#x>%{children}',
206
+ name: self.class.name,
207
+ id: object_id,
208
+ attrs: @attributes,
209
+ children: children
232
210
  end
233
211
  end
234
- end
212
+ end
@@ -1,3 +1,3 @@
1
1
  module RichText
2
- VERSION = "0.1.0"
2
+ VERSION = '0.2.0'.freeze
3
3
  end
data/lib/richtext.rb CHANGED
@@ -3,16 +3,13 @@ require 'richtext/node'
3
3
  require 'richtext/document/entry'
4
4
  require 'richtext/document'
5
5
 
6
- module RichText
7
-
6
+ module RichText
8
7
  end
9
8
 
10
-
11
9
  # RichText
12
10
  #
13
- # Convenience method for creating RichText objects. Calling RichText(obj) is
11
+ # Convenience method for creating RichText objects. Calling RichText(obj) is
14
12
  # equivalent to RichText::Document.new(obj).
15
-
16
- def RichText string
13
+ def RichText(string)
17
14
  RichText::Document.new string
18
15
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: richtext
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sebastian Lindberg
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-07-01 00:00:00.000000000 Z
11
+ date: 2016-07-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler