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.
- checksums.yaml +7 -0
- data/lib/cmless.rb +145 -0
- 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:
|