soupcms-cli 0.5.2.rc1
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/.gitignore +19 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +43 -0
- data/LICENSE.txt +22 -0
- data/README.md +11 -0
- data/Rakefile +9 -0
- data/bin/soupcms +7 -0
- data/lib/soupcms/cli/colorize.rb +167 -0
- data/lib/soupcms/cli/front_matter_parser.rb +22 -0
- data/lib/soupcms/cli/model/base.rb +145 -0
- data/lib/soupcms/cli/model/chapter.rb +55 -0
- data/lib/soupcms/cli/model/markdown.rb +45 -0
- data/lib/soupcms/cli/model/page.rb +70 -0
- data/lib/soupcms/cli/model/post.rb +14 -0
- data/lib/soupcms/cli/model/yaml.rb +18 -0
- data/lib/soupcms/cli/resolve_file_reference.rb +35 -0
- data/lib/soupcms/cli/version.rb +5 -0
- data/lib/soupcms/cli.rb +14 -0
- data/lib/soupcms/soupcms_cli.rb +128 -0
- data/lib/templates/Gemfile +18 -0
- data/lib/templates/Procfile +1 -0
- data/lib/templates/blog/my-first-post.md +23 -0
- data/lib/templates/pages/about.md +6 -0
- data/lib/templates/pages/blog-post.yml +79 -0
- data/lib/templates/pages/default.yml +82 -0
- data/lib/templates/pages/home.yml +26 -0
- data/lib/templates/pages/posts.yml +23 -0
- data/lib/templates/public/blog/posts/images/my-first-post/1-post-image.png +0 -0
- data/lib/templates/public/blog/posts/images/my-first-post.png +0 -0
- data/lib/templates/public/favicon.png +0 -0
- data/lib/templates/schemaless/footer.yml +9 -0
- data/lib/templates/schemaless/navigation.yml +25 -0
- data/lib/templates/schemaless/social-toolbar.yml +7 -0
- data/lib/templates/single-app-config.ru +54 -0
- data/soupcms-cli.gemspec +25 -0
- data/spec/sopucms-site/dummy_spec.rb +8 -0
- data/spec/sopucms-site/rake/front_matter_parser_spec.rb +38 -0
- data/spec/spec_helper.rb +17 -0
- metadata +133 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3b313169a52d034b9bd7067650136eab138df027
|
4
|
+
data.tar.gz: 1739690da1fb5529bf7675978f62795c296ddd29
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d1ff91352cb16456c538f97be490c8cf090ce6df4ecef1b23a2e3e28d5314a2d1c6940a5bb2ed4eff60aeefd7055a548c1d63440613f43f4c679d62d286a8fa7
|
7
|
+
data.tar.gz: 0e4169d473dff797e5a503955e30f699f37d5b83b327b5c61d8a052f681b48c82c2cff6dd80ed53f533a9be157eeb1cfd3e608219aa2f709de9a1a47453df457
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
soupcms-cli
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.1.1
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
soupcms-cli (0.5.1)
|
5
|
+
bson_ext (~> 1.10)
|
6
|
+
mongo (~> 1.10)
|
7
|
+
thor (~> 0.19)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
bson (1.10.0)
|
13
|
+
bson_ext (1.10.0)
|
14
|
+
bson (~> 1.10.0)
|
15
|
+
diff-lcs (1.2.5)
|
16
|
+
mongo (1.10.0)
|
17
|
+
bson (~> 1.10.0)
|
18
|
+
rack (1.5.2)
|
19
|
+
rack-test (0.6.2)
|
20
|
+
rack (>= 1.0)
|
21
|
+
rake (10.2.2)
|
22
|
+
rspec (3.0.0.beta2)
|
23
|
+
rspec-core (= 3.0.0.beta2)
|
24
|
+
rspec-expectations (= 3.0.0.beta2)
|
25
|
+
rspec-mocks (= 3.0.0.beta2)
|
26
|
+
rspec-core (3.0.0.beta2)
|
27
|
+
rspec-support (= 3.0.0.beta2)
|
28
|
+
rspec-expectations (3.0.0.beta2)
|
29
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
30
|
+
rspec-support (= 3.0.0.beta2)
|
31
|
+
rspec-mocks (3.0.0.beta2)
|
32
|
+
rspec-support (= 3.0.0.beta2)
|
33
|
+
rspec-support (3.0.0.beta2)
|
34
|
+
thor (0.19.1)
|
35
|
+
|
36
|
+
PLATFORMS
|
37
|
+
ruby
|
38
|
+
|
39
|
+
DEPENDENCIES
|
40
|
+
rack-test
|
41
|
+
rake
|
42
|
+
rspec (~> 3.0.0.beta2)
|
43
|
+
soupcms-cli!
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Sunit Parekh
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# sopuCMS::Cli
|
2
|
+
|
3
|
+
soupCMS is still in alpha and experiment stage. So please use it with caution. If you face any problem raise issue with error details and I will be happy to help.
|
4
|
+
|
5
|
+
[Getting started](http://blog.soupcms.com/posts/setup-blog-site) for more details on how to setup.
|
6
|
+
|
7
|
+
[Hosting soupCMS](http://blog.soupcms.com/posts/deploying-on-heroku) for more details on how to host your website built using soupCMS.
|
8
|
+
|
9
|
+
|
10
|
+
|
11
|
+
|
data/Rakefile
ADDED
data/bin/soupcms
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
#
|
2
|
+
# Colorize String class extension.
|
3
|
+
#
|
4
|
+
class String
|
5
|
+
|
6
|
+
#
|
7
|
+
# Colors Hash
|
8
|
+
#
|
9
|
+
COLORS = {
|
10
|
+
:black => 0,
|
11
|
+
:red => 1,
|
12
|
+
:green => 2,
|
13
|
+
:yellow => 3,
|
14
|
+
:blue => 4,
|
15
|
+
:magenta => 5,
|
16
|
+
:cyan => 6,
|
17
|
+
:white => 7,
|
18
|
+
:default => 9,
|
19
|
+
|
20
|
+
:light_black => 60,
|
21
|
+
:light_red => 61,
|
22
|
+
:light_green => 62,
|
23
|
+
:light_yellow => 63,
|
24
|
+
:light_blue => 64,
|
25
|
+
:light_magenta => 65,
|
26
|
+
:light_cyan => 66,
|
27
|
+
:light_white => 67
|
28
|
+
}
|
29
|
+
|
30
|
+
#
|
31
|
+
# Modes Hash
|
32
|
+
#
|
33
|
+
MODES = {
|
34
|
+
:default => 0, # Turn off all attributes
|
35
|
+
:bold => 1, # Set bold mode
|
36
|
+
:underline => 4, # Set underline mode
|
37
|
+
:blink => 5, # Set blink mode
|
38
|
+
:swap => 7, # Exchange foreground and background colors
|
39
|
+
:hide => 8 # Hide text (foreground color would be the same as background)
|
40
|
+
}
|
41
|
+
|
42
|
+
REGEXP_PATTERN = /\033\[([0-9]+);([0-9]+);([0-9]+)m(.+?)\033\[0m|([^\033]+)/
|
43
|
+
COLOR_OFFSET = 30
|
44
|
+
BACKGROUND_OFFSET = 40
|
45
|
+
|
46
|
+
public
|
47
|
+
|
48
|
+
#
|
49
|
+
# Change color of string
|
50
|
+
#
|
51
|
+
# Examples:
|
52
|
+
#
|
53
|
+
# puts "This is blue".colorize(:blue)
|
54
|
+
# puts "This is light blue".colorize(:light_blue)
|
55
|
+
# puts "This is also blue".colorize(:color => :blue)
|
56
|
+
# puts "This is light blue with red background".colorize(:color => :light_blue, :background => :red)
|
57
|
+
# puts "This is light blue with red background".colorize(:light_blue ).colorize( :background => :red)
|
58
|
+
# puts "This is blue text on red".blue.on_red
|
59
|
+
# puts "This is red on blue".colorize(:red).on_blue
|
60
|
+
# puts "This is red on blue and underline".colorize(:red).on_blue.underline
|
61
|
+
# puts "This is blue text on red".blue.on_red.blink
|
62
|
+
# puts "This is uncolorized".blue.on_red.uncolorize
|
63
|
+
#
|
64
|
+
def colorize(params)
|
65
|
+
begin
|
66
|
+
require 'Win32/Console/ANSI' if RUBY_PLATFORM =~ /win32/
|
67
|
+
rescue LoadError
|
68
|
+
raise 'You must gem install win32console to use colorize on Windows'
|
69
|
+
end
|
70
|
+
|
71
|
+
self.scan(REGEXP_PATTERN).inject("") do |str, match|
|
72
|
+
match[0] ||= MODES[:default]
|
73
|
+
match[1] ||= COLORS[:default] + COLOR_OFFSET
|
74
|
+
match[2] ||= COLORS[:default] + BACKGROUND_OFFSET
|
75
|
+
match[3] ||= match[4]
|
76
|
+
|
77
|
+
if (params.instance_of?(Hash))
|
78
|
+
match[0] = MODES[params[:mode]] if params[:mode] && MODES[params[:mode]]
|
79
|
+
match[1] = COLORS[params[:color]] + COLOR_OFFSET if params[:color] && COLORS[params[:color]]
|
80
|
+
match[2] = COLORS[params[:background]] + BACKGROUND_OFFSET if params[:background] && COLORS[params[:background]]
|
81
|
+
elsif (params.instance_of?(Symbol))
|
82
|
+
match[1] = COLORS[params] + COLOR_OFFSET if params && COLORS[params]
|
83
|
+
end
|
84
|
+
|
85
|
+
str << "\033[#{match[0]};#{match[1]};#{match[2]}m#{match[3]}\033[0m"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
# Return uncolorized string
|
91
|
+
#
|
92
|
+
def uncolorize
|
93
|
+
self.scan(REGEXP_PATTERN).inject("") do |str, match|
|
94
|
+
str << (match[3] || match[4])
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
#
|
99
|
+
# Return true if string is colorized
|
100
|
+
#
|
101
|
+
def colorized?
|
102
|
+
self.scan(REGEXP_PATTERN).reject do |match|
|
103
|
+
match.last
|
104
|
+
end.any?
|
105
|
+
end
|
106
|
+
|
107
|
+
#
|
108
|
+
# Make some color and on_color methods
|
109
|
+
#
|
110
|
+
COLORS.each_key do |key|
|
111
|
+
next if key == :default
|
112
|
+
|
113
|
+
define_method key do
|
114
|
+
self.colorize(:color => key)
|
115
|
+
end
|
116
|
+
|
117
|
+
define_method "on_#{key}" do
|
118
|
+
self.colorize(:background => key)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
#
|
123
|
+
# Methods for modes
|
124
|
+
#
|
125
|
+
MODES.each_key do |key|
|
126
|
+
next if key == :default
|
127
|
+
|
128
|
+
define_method key do
|
129
|
+
self.colorize(:mode => key)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class << self
|
134
|
+
|
135
|
+
#
|
136
|
+
# Return array of available modes used by colorize method
|
137
|
+
#
|
138
|
+
def modes
|
139
|
+
MODES.keys
|
140
|
+
end
|
141
|
+
|
142
|
+
#
|
143
|
+
# Return array of available colors used by colorize method
|
144
|
+
#
|
145
|
+
def colors
|
146
|
+
COLORS.keys
|
147
|
+
end
|
148
|
+
|
149
|
+
#
|
150
|
+
# Display color matrix with color names
|
151
|
+
#
|
152
|
+
def color_matrix(txt = '[X]')
|
153
|
+
size = String.colors.length
|
154
|
+
String.colors.each do |color|
|
155
|
+
String.colors.each do |back|
|
156
|
+
print txt.colorize(:color => color, :background => back)
|
157
|
+
end
|
158
|
+
puts " < #{color}"
|
159
|
+
end
|
160
|
+
String.colors.reverse.each_with_index do |back, index|
|
161
|
+
puts "#{"|".rjust(txt.length)*(size-index)} < #{back}"
|
162
|
+
end
|
163
|
+
''
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module SoupCMS
|
4
|
+
module CLI
|
5
|
+
|
6
|
+
class FrontMatterParser
|
7
|
+
|
8
|
+
def parse(string)
|
9
|
+
return {}, string if string.lines[0] && string.lines[0].chomp.strip != '---'
|
10
|
+
res = [{}, '']
|
11
|
+
res[1] = string.lstrip.gsub(/---(.*)---/m) do |match|
|
12
|
+
front_matter = $~.captures.first.strip
|
13
|
+
res[0] = YAML.load(front_matter)
|
14
|
+
''
|
15
|
+
end.strip
|
16
|
+
return res[0], res[1]
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'mongo'
|
3
|
+
require 'json'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
module SoupCMS
|
7
|
+
module CLI
|
8
|
+
module Model
|
9
|
+
|
10
|
+
class Base
|
11
|
+
|
12
|
+
def self.create_model(file)
|
13
|
+
type = File.basename(file).split('.').last
|
14
|
+
model = file.path.split('/')[2]
|
15
|
+
case type
|
16
|
+
when 'json'
|
17
|
+
SoupCMS::CLI::Model::Base.new(file).create
|
18
|
+
when 'yml'
|
19
|
+
SoupCMS::CLI::Model::Yaml.new(file).create
|
20
|
+
when 'md'
|
21
|
+
case model
|
22
|
+
when 'posts'
|
23
|
+
SoupCMS::CLI::Model::Post.new(file).create
|
24
|
+
when 'chapters'
|
25
|
+
SoupCMS::CLI::Model::Chapter.new(file).create
|
26
|
+
when 'pages'
|
27
|
+
SoupCMS::CLI::Model::Page.new(file).create
|
28
|
+
else
|
29
|
+
SoupCMS::CLI::Model::Markdown.new(file).create
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
SEVERITY_COLOR_MAP = { 'INFO' => :green,'DEBUG' => :yellow}
|
35
|
+
def initialize(file);
|
36
|
+
@file = file;
|
37
|
+
@logger = Logger.new(STDOUT)
|
38
|
+
@logger.level = ENV['verbose'] == 'true' ? Logger::DEBUG : Logger::INFO
|
39
|
+
@logger.formatter = proc do |severity, datetime, progname, msg|
|
40
|
+
"#{severity}: #{msg}\n".colorize(SEVERITY_COLOR_MAP[severity] || :red)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
attr_reader :file
|
45
|
+
|
46
|
+
def conn
|
47
|
+
return @conn if @conn
|
48
|
+
mongo_uri = ENV["MONGODB_URI_#{app_name}"] || "mongodb://localhost:27017/#{app_name}"
|
49
|
+
@conn = Mongo::MongoClient.from_uri(mongo_uri)
|
50
|
+
end
|
51
|
+
|
52
|
+
def doc_name;
|
53
|
+
File.basename(file).split('.').first
|
54
|
+
end
|
55
|
+
|
56
|
+
def slug;
|
57
|
+
doc['slug'] || doc_name
|
58
|
+
end
|
59
|
+
|
60
|
+
def type;
|
61
|
+
File.basename(file).split('.').last
|
62
|
+
end
|
63
|
+
|
64
|
+
def model;
|
65
|
+
file.path.split('/')[2]
|
66
|
+
end
|
67
|
+
|
68
|
+
def app_name;
|
69
|
+
file.path.split('/')[1]
|
70
|
+
end
|
71
|
+
|
72
|
+
def db;
|
73
|
+
conn.db
|
74
|
+
end
|
75
|
+
|
76
|
+
def coll;
|
77
|
+
db[model]
|
78
|
+
end
|
79
|
+
|
80
|
+
def hero_image
|
81
|
+
image_path = File.join('public', app_name, model, "images/#{doc_name}.*")
|
82
|
+
hero_image = Dir.glob(image_path).to_a
|
83
|
+
return File.join('/assets', app_name, model, 'images', File.basename(hero_image[0])) unless hero_image.empty?
|
84
|
+
end
|
85
|
+
|
86
|
+
def doc;
|
87
|
+
@doc ||= parse_file
|
88
|
+
end
|
89
|
+
|
90
|
+
def parse_file
|
91
|
+
document_hash = JSON.parse(file.read)
|
92
|
+
SoupCMS::CLI::ResolveFileReference.new(File.dirname(file)).parse(document_hash)
|
93
|
+
end
|
94
|
+
|
95
|
+
def old_doc
|
96
|
+
@old_doc ||= (coll.find({'doc_id' => doc['doc_id'], 'latest' => true}).to_a[0] || {})
|
97
|
+
end
|
98
|
+
|
99
|
+
def update_old_doc
|
100
|
+
coll.update({'_id' => old_doc['_id']}, {'$set' => {'latest' => false, 'state' => 'published_archive'}}) unless old_doc.empty?
|
101
|
+
end
|
102
|
+
|
103
|
+
def build
|
104
|
+
doc['doc_id'] = doc_name unless doc['doc_id']
|
105
|
+
|
106
|
+
timestamp = file.mtime.to_i
|
107
|
+
|
108
|
+
doc['publish_datetime'] = doc['publish_datetime'].to_i || timestamp
|
109
|
+
doc['version'] = timestamp unless doc['version']
|
110
|
+
doc['locale'] = 'en_US' unless doc['locale']
|
111
|
+
doc['update_datetime'] = timestamp
|
112
|
+
doc['create_datetime'] = (old_doc.empty? ? timestamp : old_doc['create_datetime'])
|
113
|
+
doc['create_by'] = 'seed' unless doc['create_by']
|
114
|
+
|
115
|
+
doc['state'] = publish_in_future? ? 'draft' : 'published' unless doc['state']
|
116
|
+
doc['latest'] = true unless doc['latest']
|
117
|
+
|
118
|
+
doc['slug'] = slug unless doc['slug']
|
119
|
+
doc['hero_image'] = {'url' => hero_image} if hero_image
|
120
|
+
end
|
121
|
+
|
122
|
+
def publish_in_future?
|
123
|
+
doc['publish_datetime'] > Time.now.to_i
|
124
|
+
end
|
125
|
+
|
126
|
+
def create
|
127
|
+
build
|
128
|
+
if doc['update_datetime'] == old_doc['update_datetime']
|
129
|
+
@logger.debug "Skipping document '#{file.path}' since no changes"
|
130
|
+
else
|
131
|
+
@logger.info "Inserting document '#{file.path}'"
|
132
|
+
@logger.debug "\n #{JSON.pretty_generate(doc)}"
|
133
|
+
coll.insert(doc)
|
134
|
+
update_old_doc
|
135
|
+
end
|
136
|
+
conn.close
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module SoupCMS
|
2
|
+
module CLI
|
3
|
+
module Model
|
4
|
+
|
5
|
+
class Chapter < SoupCMS::CLI::Model::Markdown
|
6
|
+
|
7
|
+
def release
|
8
|
+
file.path.split('/')[3]
|
9
|
+
end
|
10
|
+
|
11
|
+
def doc_name
|
12
|
+
document_name = super
|
13
|
+
document_name.match('^[\d]-').post_match
|
14
|
+
end
|
15
|
+
|
16
|
+
def chapter_number
|
17
|
+
File.basename(file).match('^[\d]')[0].to_i
|
18
|
+
end
|
19
|
+
|
20
|
+
def old_doc
|
21
|
+
@old_doc ||= (coll.find({'doc_id' => doc['doc_id'], 'release' => doc['release'], 'latest' => true}).to_a[0] || {})
|
22
|
+
end
|
23
|
+
|
24
|
+
def update_old_doc
|
25
|
+
coll.update({'_id' => old_doc['_id'], 'release' => old_doc['release']}, {'$set' => {'latest' => false}}) unless old_doc.empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def build
|
30
|
+
doc['release'] = release
|
31
|
+
super
|
32
|
+
doc['chapter_number'] = chapter_number
|
33
|
+
build_chapter_links
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_chapter_links
|
37
|
+
chapters = Dir.glob(File.join(File.dirname(file), '/*.{json,md,yml}')).to_a
|
38
|
+
index = chapters.index(file.path)
|
39
|
+
if index > 0
|
40
|
+
model = SoupCMS::CLI::Model::Chapter.new(File.new(chapters[index-1]))
|
41
|
+
doc['prev_chapter'] = {'label' => model.title, 'link' => {'model_name' => 'chapters', 'match' => {'slug' => model.slug}}}
|
42
|
+
end
|
43
|
+
if index < (chapters.size-1)
|
44
|
+
model = SoupCMS::CLI::Model::Chapter.new(File.new(chapters[index+1]))
|
45
|
+
doc['next_chapter'] = {'label' => model.title, 'link' => {'model_name' => 'chapters', 'match' => {'slug' => model.slug}}}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module SoupCMS
|
2
|
+
module CLI
|
3
|
+
module Model
|
4
|
+
|
5
|
+
class Markdown < SoupCMS::CLI::Model::Base
|
6
|
+
|
7
|
+
def content_flavor;
|
8
|
+
File.basename(file).split('.').size > 2 ? File.basename(file).split('.')[1] : 'kramdown'
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse_file
|
12
|
+
@attributes, @content = SoupCMS::CLI::FrontMatterParser.new.parse(file.read)
|
13
|
+
doc = {'content' => {'type' => 'markdown', 'flavor' => content_flavor, 'value' => @content}}
|
14
|
+
doc.merge @attributes
|
15
|
+
end
|
16
|
+
|
17
|
+
def build
|
18
|
+
super
|
19
|
+
doc['title'] = title unless doc['title']
|
20
|
+
doc['description'] = description unless doc['description']
|
21
|
+
end
|
22
|
+
|
23
|
+
def title
|
24
|
+
content_lines = doc['content']['value'].lines
|
25
|
+
doc_title = content_lines.first.chomp
|
26
|
+
doc['content']['value'] = content_lines[2] ? content_lines[2..-1].join("\n") : ''
|
27
|
+
doc_title.gsub('_', ' ').gsub('#', '').strip
|
28
|
+
end
|
29
|
+
|
30
|
+
def description
|
31
|
+
post_description = ''
|
32
|
+
content_lines = doc['content']['value'].lines
|
33
|
+
index = 0
|
34
|
+
while post_description.length < 300 && content_lines[index] do
|
35
|
+
post_description.concat(content_lines[index].chomp.gsub(/\A[\d_\W]+|[\d_\W]+\Z/, ''))
|
36
|
+
index += 1
|
37
|
+
end
|
38
|
+
post_description + '...'
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module SoupCMS
|
2
|
+
module CLI
|
3
|
+
module Model
|
4
|
+
|
5
|
+
class Page < SoupCMS::CLI::Model::Markdown
|
6
|
+
|
7
|
+
def parse_file
|
8
|
+
@attributes, @content = SoupCMS::CLI::FrontMatterParser.new.parse(file.read)
|
9
|
+
{
|
10
|
+
'areas' => [
|
11
|
+
{
|
12
|
+
'name' => 'body',
|
13
|
+
'modules' => [
|
14
|
+
{
|
15
|
+
'recipes' => [
|
16
|
+
{
|
17
|
+
'type' => 'inline',
|
18
|
+
'data' => {
|
19
|
+
'content' => {
|
20
|
+
'type' => 'markdown',
|
21
|
+
'flavor' => content_flavor,
|
22
|
+
'value' => @content
|
23
|
+
}
|
24
|
+
},
|
25
|
+
'return' => 'article'
|
26
|
+
}
|
27
|
+
],
|
28
|
+
'template' => {
|
29
|
+
'type' => 'slim',
|
30
|
+
'name' => 'bootstrap/article'
|
31
|
+
}
|
32
|
+
}
|
33
|
+
]
|
34
|
+
}
|
35
|
+
]
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def title
|
40
|
+
return doc['title'] if doc['title']
|
41
|
+
|
42
|
+
data = doc['areas'][0]['modules'][0]['recipes'][0]['data']
|
43
|
+
content_lines = data['content']['value'].lines
|
44
|
+
doc_title = content_lines.first.chomp
|
45
|
+
data['content']['value'] = content_lines[2] ? content_lines[2..-1].join("\n") : ''
|
46
|
+
doc_title = doc_title.gsub('_', ' ').gsub('#', '').strip
|
47
|
+
data['title'] = doc_title
|
48
|
+
doc_title
|
49
|
+
end
|
50
|
+
|
51
|
+
def description
|
52
|
+
return doc['description'] if doc['description']
|
53
|
+
|
54
|
+
post_description = ''
|
55
|
+
data = doc['areas'][0]['modules'][0]['recipes'][0]['data']
|
56
|
+
content_lines = data['content']['value'].lines
|
57
|
+
index = 0
|
58
|
+
while post_description.length < 300 && content_lines[index] do
|
59
|
+
post_description.concat(content_lines[index].chomp.gsub(/\A[\d_\W]+|[\d_\W]+\Z/, ''))
|
60
|
+
index += 1
|
61
|
+
end
|
62
|
+
post_description + '...'
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|