md_transformer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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: []