soupcms-cli 0.5.2.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +5 -0
  7. data/Gemfile +12 -0
  8. data/Gemfile.lock +43 -0
  9. data/LICENSE.txt +22 -0
  10. data/README.md +11 -0
  11. data/Rakefile +9 -0
  12. data/bin/soupcms +7 -0
  13. data/lib/soupcms/cli/colorize.rb +167 -0
  14. data/lib/soupcms/cli/front_matter_parser.rb +22 -0
  15. data/lib/soupcms/cli/model/base.rb +145 -0
  16. data/lib/soupcms/cli/model/chapter.rb +55 -0
  17. data/lib/soupcms/cli/model/markdown.rb +45 -0
  18. data/lib/soupcms/cli/model/page.rb +70 -0
  19. data/lib/soupcms/cli/model/post.rb +14 -0
  20. data/lib/soupcms/cli/model/yaml.rb +18 -0
  21. data/lib/soupcms/cli/resolve_file_reference.rb +35 -0
  22. data/lib/soupcms/cli/version.rb +5 -0
  23. data/lib/soupcms/cli.rb +14 -0
  24. data/lib/soupcms/soupcms_cli.rb +128 -0
  25. data/lib/templates/Gemfile +18 -0
  26. data/lib/templates/Procfile +1 -0
  27. data/lib/templates/blog/my-first-post.md +23 -0
  28. data/lib/templates/pages/about.md +6 -0
  29. data/lib/templates/pages/blog-post.yml +79 -0
  30. data/lib/templates/pages/default.yml +82 -0
  31. data/lib/templates/pages/home.yml +26 -0
  32. data/lib/templates/pages/posts.yml +23 -0
  33. data/lib/templates/public/blog/posts/images/my-first-post/1-post-image.png +0 -0
  34. data/lib/templates/public/blog/posts/images/my-first-post.png +0 -0
  35. data/lib/templates/public/favicon.png +0 -0
  36. data/lib/templates/schemaless/footer.yml +9 -0
  37. data/lib/templates/schemaless/navigation.yml +25 -0
  38. data/lib/templates/schemaless/social-toolbar.yml +7 -0
  39. data/lib/templates/single-app-config.ru +54 -0
  40. data/soupcms-cli.gemspec +25 -0
  41. data/spec/sopucms-site/dummy_spec.rb +8 -0
  42. data/spec/sopucms-site/rake/front_matter_parser_spec.rb +38 -0
  43. data/spec/spec_helper.rb +17 -0
  44. 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
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ InstalledFiles
7
+ _yardoc
8
+ coverage
9
+ doc/
10
+ lib/bundler/man
11
+ pkg
12
+ rdoc
13
+ spec/reports
14
+ test/tmp
15
+ test/version_tmp
16
+ tmp
17
+ .idea
18
+ data
19
+ config.ru
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
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
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.0
5
+ - 2.1.1
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :test do
6
+ gem 'rspec', '~> 3.0.0.beta2'
7
+ gem 'rake'
8
+ gem 'rack-test'
9
+ end
10
+
11
+
12
+
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
@@ -0,0 +1,9 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
8
+
9
+
data/bin/soupcms ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require 'rubygems'
5
+ require 'soupcms/cli'
6
+
7
+ SoupCMSCLI.start(ARGV)
@@ -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
@@ -0,0 +1,14 @@
1
+ module SoupCMS
2
+ module CLI
3
+ module Model
4
+
5
+ class Post < SoupCMS::CLI::Model::Markdown
6
+
7
+
8
+
9
+ end
10
+
11
+
12
+ end
13
+ end
14
+ end