md_transformer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b604c25943a7737b8dc80a7a8846f39ddf30f0fb6a73090b57e4589684cb3f3d
4
+ data.tar.gz: 2f928958268e5521405d36ccb57edbbe68defea3c8d041dd7ab7f05f3d5bed66
5
+ SHA512:
6
+ metadata.gz: a988fd9844215362d7ceb0042257e4d5065d3e8a5bd30df22f6847d3a7deca84f6bcbea77e74df95669aee43624c13bc75d9fa19e90d37516d8340e1e8c16745
7
+ data.tar.gz: 023a3e3a12ef3d94b426f074eeebc512f5b2dd9093914155514499e102538bc584c865597e3d8fce2efb494a163aa447c7994601c78cde9bcb9befab7f56e184
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,11 @@
1
+ Metrics/BlockLength:
2
+ ExcludedMethods: ['describe', 'context']
3
+
4
+ Metrics/ClassLength:
5
+ Max: 200
6
+
7
+ Metrics/LineLength:
8
+ Max: 120
9
+
10
+ Layout/EndOfLine:
11
+ EnforcedStyle: lf
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.5.3
7
+ before_install: gem install bundler -v 2.0.1
data/CHANGELOG.md ADDED
@@ -0,0 +1,4 @@
1
+ # md_transformer Changelog
2
+
3
+ ## 0.1.0 (04/14/2019)
4
+ * Initial release
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in md_transformer.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,55 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ md_transformer (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ast (2.4.0)
10
+ diff-lcs (1.3)
11
+ jaro_winkler (1.5.2)
12
+ parallel (1.17.0)
13
+ parser (2.6.2.1)
14
+ ast (~> 2.4.0)
15
+ psych (3.1.0-x64-mingw32)
16
+ rainbow (3.0.0)
17
+ rake (10.5.0)
18
+ rspec (3.8.0)
19
+ rspec-core (~> 3.8.0)
20
+ rspec-expectations (~> 3.8.0)
21
+ rspec-mocks (~> 3.8.0)
22
+ rspec-core (3.8.0)
23
+ rspec-support (~> 3.8.0)
24
+ rspec-expectations (3.8.2)
25
+ diff-lcs (>= 1.2.0, < 2.0)
26
+ rspec-support (~> 3.8.0)
27
+ rspec-mocks (3.8.0)
28
+ diff-lcs (>= 1.2.0, < 2.0)
29
+ rspec-support (~> 3.8.0)
30
+ rspec-support (3.8.0)
31
+ rubocop (0.67.2)
32
+ jaro_winkler (~> 1.5.1)
33
+ parallel (~> 1.10)
34
+ parser (>= 2.5, != 2.5.1.1)
35
+ psych (>= 3.1.0)
36
+ rainbow (>= 2.2.2, < 4.0)
37
+ ruby-progressbar (~> 1.7)
38
+ unicode-display_width (>= 1.4.0, < 1.6)
39
+ ruby-progressbar (1.10.0)
40
+ unicode-display_width (1.5.0)
41
+ yard (0.9.19)
42
+
43
+ PLATFORMS
44
+ x64-mingw32
45
+
46
+ DEPENDENCIES
47
+ bundler (~> 2.0)
48
+ md_transformer!
49
+ rake (~> 10.0)
50
+ rspec (~> 3.0)
51
+ rubocop (~> 0.67)
52
+ yard (~> 0.9)
53
+
54
+ BUNDLED WITH
55
+ 2.0.1
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 wheatevo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,158 @@
1
+ # md_transformer
2
+
3
+ The md_transformer gem provides a way to read, modify, and write Markdown much as a hash with header keys and markdown content values.
4
+
5
+ This code:
6
+ ```ruby
7
+ require 'md_transformer'
8
+
9
+ # Create a new markdown object and manipulate it
10
+ md = MdTransformer.markdown("# md_transformer\nThe md_transformer gem...\n")
11
+ md['md_transformer']['Installation'] = "Add this line to your application's Gemfile"
12
+ md['md_transformer']['Usage'] = ''
13
+ md['md_transformer']['Usage']['Creating a Markdown object'] = 'The `MdTransformer`...'
14
+ md.write('my_new_readme.md')
15
+ ```
16
+
17
+ Generates a Markdown file at `my_new_readme.md` with the following content:
18
+ ```md
19
+ # md_transformer
20
+ The md_transformer gem...
21
+ ## Installation
22
+ Add this line to your application's Gemfile
23
+ ## Usage
24
+ ### Creating a Markdown object
25
+ The `MdTransformer`...
26
+ ```
27
+
28
+ ## Installation
29
+
30
+ Add this line to your application's Gemfile:
31
+
32
+ ```ruby
33
+ gem 'md_transformer'
34
+ ```
35
+
36
+ And then execute:
37
+
38
+ $ bundle
39
+
40
+ Or install it yourself as:
41
+
42
+ $ gem install md_transformer
43
+
44
+ ## Usage
45
+
46
+ ### Creating a Markdown object
47
+ The `MdTransformer` module has several helper methods for creating a new `MdTransformer::Markdown` object.
48
+
49
+ #### Create from Content
50
+ Use the `MdTransformer.markdown` method to create a new `MdTransformer::Markdown` object from a string.
51
+ ```ruby
52
+ md_object = MdTransformer.markdown("# My document\nInformation is good.\n\n## More information\n> Detailed data\n")
53
+ ```
54
+
55
+ > The `md` method is also available as an alias to `markdown`
56
+
57
+ #### Create from File
58
+ Use the `MdTransformer.markdown_file` method to create a new `MdTransformer::Markdown` object from a file.
59
+ ```ruby
60
+ md_object = MdTransformer.markdown_file('README.md')
61
+ md_object = MdTransformer.markdown_file('/full/path/to/README.md')
62
+ ```
63
+ > The `md_file` method is also available as an alias to `markdown_file`
64
+
65
+ ### Manipulating the Markdown document
66
+ The `MdTransformer::Markdown` object can be used similarly to a `Hash`. Headers are treated as keys that refer to sections of the Markdown document.
67
+
68
+ #### Markdown can be Accessed as a Hash
69
+ ```ruby
70
+ # Create Markdown object from a string
71
+ md1 = MdTransformer.markdown("# Title\nContent\n## Sub-heading\nSub-content\n## Sub-heading 2\nMore content\n")
72
+
73
+ # Get all heading keys for the Title heading
74
+ md1['Title'].keys
75
+ # => ["Sub-heading", "Sub-heading 2"]
76
+
77
+ # Get the content of the Title heading
78
+ md1['Title'].content
79
+ # => "Content\n"
80
+
81
+ # Get the content of the Sub-heading
82
+ md1['Title']['Sub-heading'].content
83
+ # => "Sub-content\n"
84
+
85
+ # Dig for a key
86
+ md1.dig('Title', 'Sub-heading').content
87
+ # => "Sub-content\n"
88
+
89
+ # Output a section as a string
90
+ md1['Title'].to_s
91
+ # => "# Title\nContent\n## Sub-heading\nSub-content\n## Sub-heading 2\nMore content\n"
92
+
93
+ # Update the content of a section
94
+ md1['Title']['Sub-heading'] = "Here is new content!\n"
95
+ md1['Title']['Sub-heading'].content
96
+ # => "Here is new content!"
97
+ ```
98
+
99
+ #### Markdown can be Compared
100
+ ```ruby
101
+ # Create Markdown objects from a string
102
+ md1 = MdTransformer.markdown("# Title\nContent\n## Sub-heading\nSub-content\n")
103
+ md2 = MdTransformer.markdown("# Title\nContent\n## Sub-heading\nSub-content 2\n")
104
+
105
+ md1 == md2
106
+ # => false
107
+
108
+ # Set the ## Sub-heading content on md2 to match md1
109
+ md2['Title']['Sub-heading'] = 'Sub-content'
110
+
111
+ md1 == md2
112
+ # => true
113
+ ```
114
+
115
+ #### Markdown can be Enumerated
116
+ ```ruby
117
+ # Create Markdown object from a string
118
+ md1 = MdTransformer.markdown("# Title\nContent\n## Sub-heading\nSub-content\n## Sub-heading 2\nMore content\n")
119
+
120
+ md1['Title'].map { |k, v| "#{k}!" }
121
+ # => ["Sub-heading!", "Sub-heading 2!"]
122
+ ```
123
+
124
+ ### Rendering the Markdown document
125
+
126
+ #### Viewing the content of the document
127
+ Use the `to_s` method to convert the `MdTransformer::Markdown` object into valid Markdown content.
128
+ ```ruby
129
+ # Show the current markdown document
130
+ puts md_object
131
+
132
+ # Assign the markdown document content to a string
133
+ string_content = md_object.to_s
134
+ ```
135
+
136
+ #### Writing the document to a file
137
+ Use the `write` method to write the current `MdTransformer::Markdown` object's content to a file.
138
+ ```ruby
139
+ md_object.write('test.md')
140
+ md_object.write('/my/long/directory/README.md')
141
+ ```
142
+
143
+ ### Need More Help?
144
+ Please take a look at the [API documentation](https://www.rubydoc.info/github/wheatevo/md_transformer/master).
145
+
146
+ ## Development
147
+
148
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
149
+
150
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
151
+
152
+ ## Contributing
153
+
154
+ Bug reports and pull requests are welcome on GitHub at https://github.com/wheatevo/md_transformer.
155
+
156
+ ## License
157
+
158
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'md_transformer'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,86 @@
1
+ module MdTransformer
2
+ class Markdown
3
+ # Class representing a section of a markdown file (header with content)
4
+ class Section
5
+ # @return [String] the title of the section
6
+ attr_reader :title
7
+
8
+ # @return [Integer] the precedence level of the section
9
+ attr_reader :level
10
+
11
+ # @return [Range] the section header's location in the original content
12
+ attr_reader :header_location
13
+
14
+ # @return [String] the section's content
15
+ attr_reader :content
16
+
17
+ # Creates the Section object
18
+ # @param title [String] the title of the section
19
+ # @param options [Hash] the options hash
20
+ # @option options [Integer] :level (0) the precedence level of the section
21
+ # @option options [Range] :header_location (0..0) the section header's location in the original content
22
+ # @option options [String] :content ('') the content of the section
23
+ # @return [MdTransformer::Markdown::Section] the new Section object
24
+ def initialize(title, options = {})
25
+ @title = title
26
+ @level = options[:level] || 0
27
+ @header_location = options[:header_location].nil? ? 0..0 : options[:header_location]
28
+ @content = options[:content] || ''
29
+ end
30
+
31
+ # Generates an array of Section objects from string Markdown content
32
+ # @param content [String] the markdown content to parse
33
+ # @return [Array] the array of generated Section objects
34
+ def self.generate_sections(content)
35
+ headers = header_locations(content)
36
+ headers.each_with_index do |h, i|
37
+ content_end = content.length
38
+ headers[i] = { header: h, content: ((h.end + 1)..(content_end - 1)) }
39
+ end
40
+ headers.map! { |h| create_section_from_header(h, content) }
41
+ end
42
+
43
+ class << self
44
+ private
45
+
46
+ # Gathers header locations for given content
47
+ # @param content [String] the markdown content to parse
48
+ # @return [Array] the array of all header locations given as ranges
49
+ def header_locations(content)
50
+ code_ranges = code_locations(content)
51
+ hdr_regex = /^(\#{1,6}\s+.*)$/
52
+ hdr_ranges = content.enum_for(:scan, hdr_regex).map { Regexp.last_match.begin(0)..Regexp.last_match.end(0) }
53
+ hdr_ranges.reject { |hdr_range| code_ranges.any? { |code_range| code_range.include?(hdr_range.begin) } }
54
+ end
55
+
56
+ # Gathers code block locations for given content
57
+ # @param content [String] the markdown content to parse
58
+ # @return [Array] the array of all code block locations given as ranges
59
+ def code_locations(content)
60
+ block_regex = /^([`~]{3}.*?^[`~]{3})$/m
61
+ content.enum_for(:scan, block_regex).map { Regexp.last_match.begin(0)..Regexp.last_match.end(0) }
62
+ end
63
+
64
+ # Creates a new Section object from a header hash
65
+ # @param header [Hash] hash containing :header and :content keys representing header and content locations
66
+ # @param content [String] the markdown content to parse
67
+ # @return [MdTransformer::Markdown::Section] the new Section object
68
+ def create_section_from_header(header, content)
69
+ Section.new(
70
+ content[header[:header]].match(/^#+\s+(.*)$/)[1],
71
+ level: header_level(content[header[:header]]),
72
+ header_location: header[:header].begin..header[:content].end,
73
+ content: content[header[:content]]
74
+ )
75
+ end
76
+
77
+ # Determines the precedence level of a header
78
+ # @param header [String] the header string to parse
79
+ # @return [Integer] the calculated precedence level
80
+ def header_level(header)
81
+ header.split.first.count('#')
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,219 @@
1
+ require 'fileutils'
2
+ require 'md_transformer/markdown/section'
3
+
4
+ module MdTransformer
5
+ # Class representing a parsed markdown file
6
+ class Markdown
7
+ include Comparable
8
+ include Enumerable
9
+
10
+ # @return [String] the title of the Markdown object
11
+ attr_accessor :title
12
+
13
+ # @return [String] the content of the Markdown object
14
+ attr_reader :content
15
+
16
+ # @return [Array] the array of child objects
17
+ attr_reader :children
18
+
19
+ # @return [MdTransformer::Markdown, nil] nil or the parent of the current Markdown object
20
+ attr_reader :parent
21
+
22
+ # The lowest valid precedence of a header, allows for up to H6 (###### Header)
23
+ LOWEST_PRECEDENCE = 6
24
+
25
+ # Creates a new Markdown object
26
+ # @param source [String] the markdown content or path to a markdown file
27
+ # @param options [Hash] the options hash
28
+ # @option options [Boolean] :file whether to treat the passed source as a file path
29
+ # @option options [MdTransformer::Markdown] :parent the parent of the new object
30
+ # @option options [String] :title the title of the new object
31
+ # @return [MdTransformer::Markdown] the new Markdown object
32
+ def initialize(source = '', options = {})
33
+ @parent = options[:parent]
34
+ @title = options[:title] || ''
35
+ if options[:file]
36
+ raise InvalidMarkdownPath, "Could not find markdown file at #{source}" unless File.exist?(source)
37
+
38
+ source = File.read(source)
39
+ @title ||= options[:file]
40
+ end
41
+ parse!(source)
42
+ end
43
+
44
+ # Updates the current object's Markdown content
45
+ # @param value [String] the new content
46
+ # @return [String] the newly added content
47
+ def content=(value)
48
+ # Reflow the content headers based on the child's levels (raise exception if level exceeds 6)
49
+ m = Markdown.new(value.to_s)
50
+
51
+ # Reassign the parent of the children to the current object and ensure the new depth is valid
52
+ @children = m.children
53
+ @children.each do |c|
54
+ c.instance_variable_set(:@parent, self)
55
+ validate_levels(c)
56
+ end
57
+
58
+ @content = m.content
59
+ end
60
+
61
+ # Gets all child object keys
62
+ # @return [Array] the array of child keys
63
+ def keys
64
+ @children.map(&:title)
65
+ end
66
+
67
+ # Checks for whether a child object has a given key
68
+ # @param key [String] the key of the child object
69
+ # @return [Boolean] whether the passed key exists
70
+ def key?(key)
71
+ !self[key].nil?
72
+ end
73
+
74
+ # Gets all child object values
75
+ # @return [Array] the array of child objects
76
+ def values
77
+ @children
78
+ end
79
+
80
+ # Checks for whether a child object has a given value
81
+ # @param value [String] the value to check for
82
+ # @return [Boolean] whether the passed value exists
83
+ def value?(value)
84
+ !@children.find { |c| c == value }.nil?
85
+ end
86
+
87
+ # Digs through the hash for the child object at the given nested key(s)
88
+ # @param key [String] the first key to check
89
+ # @param rest [Array<String>] any number of nested string keys for which to find
90
+ # @return [MdTransformer::Markdown, nil] the found child object or nil
91
+ def dig(key, *rest)
92
+ value = self[key]
93
+ return value if value.nil? || rest.empty?
94
+
95
+ value.dig(*rest)
96
+ end
97
+
98
+ # Retrieves the child object at a given key if it exists
99
+ # @param key [String] the key of the child object
100
+ # @return [MdTransformer::Markdown, nil] the found child object or nil
101
+ def [](key)
102
+ @children.find { |c| c.title == key }
103
+ end
104
+
105
+ # Sets the value of the child object at a given key. If the key does not exist, a new child object is created.
106
+ # @param key [String] the key of the child object
107
+ # @param value [String] the new value of the child object
108
+ # @return [String] the newly assigned value
109
+ def []=(key, value)
110
+ child = self[key] || Markdown.new('', title: key, parent: self)
111
+ child.content = value.to_s
112
+ @children.push(child) unless key?(key)
113
+ end
114
+
115
+ # Creates a string representing the markdown document's content from current content and all child content
116
+ # @param options [Hash] the options hash
117
+ # @option options [Boolean] :title (true) whether to include the title of the current object in the output
118
+ # @return [String] the constructed Markdown string
119
+ def to_s(options = { title: true })
120
+ title_str = root? ? '' : "#{'#' * level} #{@title}\n"
121
+ md_string = "#{options[:title] ? title_str : ''}#{@content}#{@children.map(&:to_s).join}"
122
+ md_string << "\n" unless md_string.end_with?("\n")
123
+ md_string
124
+ end
125
+
126
+ # Writes the current markdown object to a file
127
+ # @param path [String] the path to the new file
128
+ # @param options [Hash] the options hash
129
+ # @option options [Boolean] :create_dir (true) whether to create the parent directories of the path
130
+ # @return [Integer] the length of the newly created file
131
+ def write(path, options: { create_dir: true })
132
+ FileUtils.mkdir_p(File.dirname(path)) if options[:create_dir]
133
+ File.write(path, to_s)
134
+ end
135
+
136
+ # Checks whether the current object is the root of the Markdown object
137
+ # @return [Boolean] whether the current object is the root
138
+ def root?
139
+ @parent.nil?
140
+ end
141
+
142
+ # Calculates the current nesting level of the Markdown object
143
+ # @return [Integer] the nesting level of the object (0 for the root, +1 for each additional level)
144
+ def level
145
+ return 0 if root?
146
+
147
+ @parent.level + 1
148
+ end
149
+
150
+ # For a block { |k, v| ... }
151
+ # @yield [k, v] Gives the key and value of the object
152
+ def each
153
+ return enum_for(__method__) unless block_given?
154
+
155
+ @children.each do |child|
156
+ yield child.title, child
157
+ end
158
+ end
159
+
160
+ # Compares Markdown objects with other objects through string conversion
161
+ # @param other [Object] object to compare
162
+ # @return [Integer] the result of the <=> operator on the string values of both objects
163
+ def <=>(other)
164
+ to_s <=> other.to_s
165
+ end
166
+
167
+ private
168
+
169
+ # Validates that all children have valid precedence levels and raises an exception if they are not
170
+ # @param child [MdTransformer::Markdown] child object ot validate
171
+ # @return [MdTransformer::Markdown] the validated child object
172
+ def validate_levels(child)
173
+ if child.level >= LOWEST_PRECEDENCE
174
+ raise HeaderTooDeep, "#{child.title} header level (h#{child.level}) is beyond h#{LOWEST_PRECEDENCE}"
175
+ end
176
+
177
+ child.children.each { |c| validate_levels(c) }
178
+ child
179
+ end
180
+
181
+ # Parses the provided markdown string content into a the current object's content and children
182
+ # @param content [String] the string Markdown content to parse
183
+ def parse!(content)
184
+ @children = []
185
+ @content = ''
186
+
187
+ # Parse all direct children and create new markdown objects
188
+ sections = Section.generate_sections(content)
189
+
190
+ # No children!
191
+ if sections.empty?
192
+ @content = content
193
+ return
194
+ end
195
+
196
+ # Populate content prior to found headers
197
+ @content = content[0..sections.first.header_location.begin - 1] if sections.first.header_location.begin > 0
198
+
199
+ parse_children!(sections)
200
+ end
201
+
202
+ # Parses all available sections into direct children of the current object
203
+ # @param sections [Array] the array of Markdown::Section objects to parse
204
+ def parse_children!(sections)
205
+ # Go through the headers sequentially to find all direct children (base on header level vs. current level)
206
+ last_child_level = LOWEST_PRECEDENCE + 1
207
+
208
+ sections.each do |s|
209
+ # Finish parsing if we encounter a sibling (same level) or aunt/uncle (higher level)
210
+ break if s.level <= level
211
+
212
+ if s.level <= last_child_level
213
+ @children.push(Markdown.new(s.content, title: s.title, parent: self))
214
+ last_child_level = s.level
215
+ end
216
+ end
217
+ end
218
+ end
219
+ end
@@ -0,0 +1,4 @@
1
+ module MdTransformer
2
+ # Current MdTransformer version
3
+ VERSION = '0.1.0'.freeze
4
+ end
@@ -0,0 +1,33 @@
1
+ require 'md_transformer/version'
2
+ require 'md_transformer/markdown'
3
+
4
+ # Module containing classes relating to parsing and using Markdown content similarly to a hash
5
+ module MdTransformer
6
+ # Base error class for MdTransformer
7
+ class Error < ::StandardError; end
8
+
9
+ # Error indicating that a passed markdown path cannot be found or read
10
+ class InvalidMarkdownPath < MdTransformer::Error; end
11
+
12
+ # Error indicating that a parsed header will exceed the maximum level (over h6)
13
+ class HeaderTooDeep < MdTransformer::Error; end
14
+
15
+ class << self
16
+ # Creates a new Markdown object from given content
17
+ # @param content [String] the markdown content
18
+ # @return [MdTransformer::Markdown] the new Markdown object
19
+ def markdown(content)
20
+ Markdown.new(content)
21
+ end
22
+
23
+ # Creates a new Markdown object from a file
24
+ # @param path [String] path to the markdown file to open
25
+ # @return [MdTransformer::Markdown] the new Markdown object
26
+ def markdown_file(path)
27
+ Markdown.new(path, file: true)
28
+ end
29
+
30
+ alias md markdown
31
+ alias md_file markdown_file
32
+ end
33
+ end
@@ -0,0 +1,41 @@
1
+ lib = File.expand_path('lib', __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'md_transformer/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'md_transformer'
7
+ spec.version = MdTransformer::VERSION
8
+ spec.authors = ['wheatevo']
9
+ spec.email = ['matthewtnewell@gmail.com']
10
+
11
+ spec.summary = 'Gem for transforming markdown files into a nested hash for easy editing and output.'
12
+ spec.description = 'Gem for transforming markdown files into a nested hash for easy editing and output.'
13
+ spec.homepage = 'https://github.com/wheatevo/md_transformer'
14
+ spec.license = 'MIT'
15
+
16
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
18
+ if spec.respond_to?(:metadata)
19
+ spec.metadata['homepage_uri'] = spec.homepage
20
+ spec.metadata['source_code_uri'] = 'https://github.com/wheatevo/md_transformer'
21
+ spec.metadata['changelog_uri'] = 'https://github.com/wheatevo/md_transformer/blob/master/CHANGELOG.md'
22
+ else
23
+ raise 'RubyGems 2.0 or newer is required to protect against ' \
24
+ 'public gem pushes.'
25
+ end
26
+
27
+ # Specify which files should be added to the gem when it is released.
28
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
29
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
30
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
31
+ end
32
+ spec.bindir = 'exe'
33
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
34
+ spec.require_paths = ['lib']
35
+
36
+ spec.add_development_dependency 'bundler', '~> 2.0'
37
+ spec.add_development_dependency 'rake', '~> 10.0'
38
+ spec.add_development_dependency 'rspec', '~> 3.0'
39
+ spec.add_development_dependency 'rubocop', '~> 0.67'
40
+ spec.add_development_dependency 'yard', '~> 0.9'
41
+ end
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: md_transformer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - wheatevo
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-04-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.67'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.67'
69
+ - !ruby/object:Gem::Dependency
70
+ name: yard
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.9'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.9'
83
+ description: Gem for transforming markdown files into a nested hash for easy editing
84
+ and output.
85
+ email:
86
+ - matthewtnewell@gmail.com
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - ".rspec"
93
+ - ".rubocop.yml"
94
+ - ".travis.yml"
95
+ - CHANGELOG.md
96
+ - Gemfile
97
+ - Gemfile.lock
98
+ - LICENSE.txt
99
+ - README.md
100
+ - Rakefile
101
+ - bin/console
102
+ - bin/setup
103
+ - lib/md_transformer.rb
104
+ - lib/md_transformer/markdown.rb
105
+ - lib/md_transformer/markdown/section.rb
106
+ - lib/md_transformer/version.rb
107
+ - md_transformer.gemspec
108
+ homepage: https://github.com/wheatevo/md_transformer
109
+ licenses:
110
+ - MIT
111
+ metadata:
112
+ homepage_uri: https://github.com/wheatevo/md_transformer
113
+ source_code_uri: https://github.com/wheatevo/md_transformer
114
+ changelog_uri: https://github.com/wheatevo/md_transformer/blob/master/CHANGELOG.md
115
+ post_install_message:
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ requirements: []
130
+ rubyforge_project:
131
+ rubygems_version: 2.7.6
132
+ signing_key:
133
+ specification_version: 4
134
+ summary: Gem for transforming markdown files into a nested hash for easy editing and
135
+ output.
136
+ test_files: []