soupcms-cli 0.5.2.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|