cmless 0.0.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.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/cmless.rb +145 -0
  3. metadata +46 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bdb15f8e0031cc6134590b4476267f2de933d79b
4
+ data.tar.gz: c9648c323e096ff9a2bf767507ebf18a1ee0e5af
5
+ SHA512:
6
+ metadata.gz: b59096de7971a1f6695581fbf8ad61c856f5df2fa0da03b4ad372f02982d03daa446d019f89ed593b26806801eb57f33166ea1c6039ac38d90bf7c9474ef862e
7
+ data.tar.gz: 9df87894d707f101b566a58d781086e7173bb50a16541a1016c1d61e1b7f84e544de8d70e80b624e86eb29f602ad7e30eb1425b3b3652db8c253fb17d9a397a8
data/lib/cmless.rb ADDED
@@ -0,0 +1,145 @@
1
+ # require_relative 'cmless/cmless'
2
+ # If this becomes multiple files, then require a subdirectory.
3
+ # but if it's just one file, it's fine here.
4
+
5
+ require 'redcarpet'
6
+ require 'singleton'
7
+ require 'nokogiri'
8
+
9
+ class Cmless
10
+
11
+ attr_reader :path
12
+ attr_reader :title
13
+
14
+ private
15
+
16
+ # You should use find_by_path rather than creating your own objects.
17
+ def initialize(file_path)
18
+ @path = self.class.path_from_file_path(file_path)
19
+ Nokogiri::HTML(Markdowner.instance.render(File.read(file_path))).tap do |doc|
20
+ @title = doc.xpath('//h1').first.remove.text
21
+
22
+ html_methods = self.class.instance_methods.
23
+ select { |method| method.to_s.match(/\_html$/) }
24
+
25
+ if html_methods.include?(:head_html)
26
+ self.instance_variable_set('@head_html', Cmless.extract_head_html(doc))
27
+ html_methods.delete(:head_html)
28
+ end
29
+
30
+ if html_methods.include?(:body_html)
31
+ self.instance_variable_set('@body_html', Cmless.extract_body_html(doc))
32
+ html_methods.delete(:body_html)
33
+ end
34
+
35
+ html_methods.each do |method|
36
+ h2_name = method.to_s.gsub(/\_html$/, '').gsub('_',' ').capitalize
37
+ variable_name = "@#{method.to_s}"
38
+ self.instance_variable_set(variable_name, Cmless.extract_html(doc, h2_name))
39
+ end
40
+
41
+ doc.text.strip.tap do |extra|
42
+ escaped = extra.gsub("\n",'\\n').gsub("\t",'\\t')
43
+ fail("#{file_path} has extra unused text: '#{escaped}'") unless extra == ''
44
+ end
45
+ end
46
+ end
47
+
48
+ public
49
+
50
+ # Instance methods:
51
+
52
+ def ancestors
53
+ @ancestors ||= begin
54
+ split = path.split('/')
55
+ (1..split.size-1).to_a.map do |i|
56
+ self.class.objects_by_path[split[0,i].join('/')]
57
+ end
58
+ end
59
+ end
60
+
61
+ def children
62
+ @children ||= begin
63
+ self.class.objects_by_path.select do |other_path, other_object|
64
+ other_path.match(/^#{path}\/[^\/]+$/) # TODO: escape
65
+ end.map do |other_path, other_object|
66
+ other_object
67
+ end
68
+ end
69
+ end
70
+
71
+
72
+ # Class methods:
73
+
74
+ def self.objects_by_path
75
+ @objects_by_path ||=
76
+ begin
77
+ unless File.directory?(self.root_path)
78
+ raise StandardError.new("#{self.root_path} is not a directory")
79
+ end
80
+ Hash[
81
+ Dir[Pathname(self.root_path) + '**/*.md'].sort.map do |path|
82
+ object = self.new(path)
83
+ [object.path, object]
84
+ end
85
+ ]
86
+ end
87
+ end
88
+
89
+ def self.find_by_path(path)
90
+ self.objects_by_path[path] || raise(IndexError.new("'#{path}' is not a valid path under '#{self.root_path}'; Expected one of #{self.objects_by_path.keys}"))
91
+ end
92
+
93
+ def self.path_from_file_path(file_path)
94
+ file_path.to_s.gsub(self.root_path.to_s+'/', '').gsub(/\.md$/, '')
95
+ end
96
+
97
+ def self.extract_html(doc, title)
98
+ following_siblings = []
99
+ doc.xpath("//h2[text()='#{title}']").first.tap do |header|
100
+ raise IndexError.new("Can't find header '#{title}'") unless header
101
+ while header.next_element && !header.next_element.name.match(/h2/) do
102
+ following_siblings.push(header.next_element.remove)
103
+ end
104
+ header.remove
105
+ end
106
+ following_siblings.map { |el| el.to_s }.join
107
+ end
108
+
109
+ def self.extract_head_html(doc)
110
+ siblings = []
111
+ body = doc.xpath('//body').first
112
+ while body.children.first && !body.children.first.name.match(/h2/)
113
+ siblings.push(body.children.first.remove)
114
+ end
115
+ siblings.map { |el| el.to_s }.join.strip
116
+ end
117
+
118
+ def self.extract_body_html(doc)
119
+ siblings = []
120
+ body = doc.xpath('//body').first
121
+ while body.children.first
122
+ siblings.push(body.children.first.remove)
123
+ end
124
+ siblings.map { |el| el.to_s }.join.strip
125
+ end
126
+
127
+
128
+ # Utility class: (This could move.)
129
+
130
+ class Markdowner
131
+ include Singleton
132
+
133
+ def initialize()
134
+ @markdown = Redcarpet::Markdown.new(
135
+ Redcarpet::Render::XHTML.new(with_toc_data: true),
136
+ autolink: true)
137
+ end
138
+
139
+ def render(md_text)
140
+ return unless md_text
141
+ @markdown.render(md_text)
142
+ end
143
+ end
144
+
145
+ end
metadata ADDED
@@ -0,0 +1,46 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cmless
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Chuck McCallum
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-07-22 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: 'CMS alternative: Maintain content in markdown / Extract HTML and data
14
+ for display'
15
+ email: chuck_mccallum@wgbh.org
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/cmless.rb
21
+ homepage: https://github.com/WGBH/cmless
22
+ licenses:
23
+ - MIT
24
+ metadata: {}
25
+ post_install_message:
26
+ rdoc_options: []
27
+ require_paths:
28
+ - lib
29
+ required_ruby_version: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ required_rubygems_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ requirements: []
40
+ rubyforge_project:
41
+ rubygems_version: 2.2.3
42
+ signing_key:
43
+ specification_version: 4
44
+ summary: CMS, but less
45
+ test_files: []
46
+ has_rdoc: