jekyll-import 0.21.0 → 0.23.0

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.
@@ -32,17 +32,17 @@ module JekyllImport
32
32
  end
33
33
 
34
34
  def self.specify_options(c)
35
- c.option "engine", "--engine ENGINE", "Database engine, (default: 'mysql', postgres also supported)"
36
- c.option "dbname", "--dbname DB", "Database name"
37
- c.option "user", "--user USER", "Database user name"
38
- c.option "password", "--password PW", "Database user's password, (default: '')"
39
- c.option "host", "--host HOST", 'Database host name (default: "localhost")'
40
- c.option "port", "--port PORT", "Custom database port connect to (optional)"
41
- c.option "blog_id", "--blog_id ID", "Specify a single Movable Type blog ID to import (default: all blogs)"
42
- c.option "categories", "--categories", "If true, save post's categories in its YAML front matter. (default: true)"
35
+ c.option "dbname", "--dbname DB", "Database name."
36
+ c.option "user", "--user USER", "Database user name."
37
+ c.option "engine", "--engine ENGINE", "Database engine ('mysql' or 'postgres'). (default: 'mysql')"
38
+ c.option "password", "--password PW", "Database user's password. (default: '')"
39
+ c.option "host", "--host HOST", "Database host name. (default: 'localhost')"
40
+ c.option "port", "--port PORT", "Custom database port connect to. (default: null)"
41
+ c.option "blog_id", "--blog_id ID", "Specify a single Movable Type blog ID to import. (default: null (all blogs))"
42
+ c.option "categories", "--categories", "When true, save post's categories in its YAML front matter. (default: true)"
43
43
  c.option "src_encoding", "--src_encoding ENCODING", "Encoding of strings from database. (default: UTF-8)"
44
44
  c.option "dest_encoding", "--dest_encoding ENCODING", "Encoding of output strings. (default: UTF-8)"
45
- c.option "comments", "--comments", "If true, output comments in _comments directory (default: false)"
45
+ c.option "comments", "--comments", "When true, output comments in `_comments` directory. (default: false)"
46
46
  end
47
47
 
48
48
  # By default this migrator will include posts for all your MovableType blogs.
@@ -242,7 +242,7 @@ module JekyllImport
242
242
  Sequel.sqlite(dbname)
243
243
  when "mysql", "postgres"
244
244
  db_connect_opts = {
245
- :host => options.fetch("host", "localhost"),
245
+ :host => options.fetch("host", "127.0.0.1"),
246
246
  :user => options.fetch("user"),
247
247
  :password => options.fetch("password", ""),
248
248
  }
@@ -0,0 +1,97 @@
1
+ module JekyllImport
2
+ module Importers
3
+ class Pebble < Importer
4
+ def self.require_deps
5
+ JekyllImport.require_with_fallback(%w(
6
+ nokogiri
7
+ safe_yaml
8
+ ))
9
+ end
10
+
11
+ def self.specify_options(c)
12
+ c.option "directory", "--directory PATH", "Pebble source directory"
13
+ end
14
+
15
+ def self.process(opts)
16
+ options = {
17
+ directory: opts.fetch("directory", "")
18
+ }
19
+
20
+ FileUtils.mkdir_p("_posts")
21
+ FileUtils.mkdir_p("_drafts")
22
+
23
+ traverse_posts_within(options[:directory]) do |file|
24
+ next if file.end_with?('categories.xml')
25
+ process_file(file)
26
+ end
27
+ end
28
+
29
+ def self.traverse_posts_within(directory, &block)
30
+ Dir.foreach(directory) do |fd|
31
+ path = File.join(directory, fd)
32
+ if fd == '.' || fd == '..'
33
+ next
34
+ elsif File.directory?(path)
35
+ traverse_posts_within(path, &block)
36
+ elsif path.end_with?('xml')
37
+ yield(path) if block_given?
38
+ else
39
+ end
40
+ end
41
+ end
42
+
43
+ def self.process_file(file)
44
+ xml = File.open(file) { |f| Nokogiri::XML(f) }
45
+ raise "There doesn't appear to be any XML items at the source (#{file}) provided." unless xml
46
+
47
+ doc = xml.xpath("blogEntry")
48
+
49
+ title = kebabize(doc.xpath('title').text).gsub('_', '-')
50
+ date = Date.parse(doc.xpath('date').text)
51
+
52
+ directory = "_posts"
53
+ name = "#{date.strftime('%Y-%m-%d')}-#{title}"
54
+
55
+ header = {
56
+ "layout" => 'post',
57
+ "title" => doc.xpath("title").text,
58
+ "tags" => doc.xpath("tags").text.split(", "),
59
+ "categories" => doc.xpath('category').text.split(', ')
60
+ }
61
+ header["render_with_liquid"] = false
62
+
63
+ path = File.join(directory, "#{name}.html")
64
+ File.open(path, "w") do |f|
65
+ f.puts header.to_yaml
66
+ f.puts "---\n\n"
67
+ f.puts doc.xpath("body").text
68
+ end
69
+
70
+ Jekyll.logger.info "Wrote file #{path} successfully!"
71
+ end
72
+
73
+ def self.kebabize(string)
74
+ kebab = '-'.freeze
75
+ string.gsub!(/[^\w\-_]+/, kebab)
76
+
77
+ unless kebab.nil? || kebab.empty?
78
+ if kebab == "-".freeze
79
+ re_duplicate_kebab = /-{2,}/
80
+ re_leading_trailing_kebab = /^-|-$/
81
+ else
82
+ re_sep = Regexp.escape(kebab)
83
+ re_duplicate_kebab = /#{re_sep}{2,}/
84
+ re_leading_trailing_kebab = /^#{re_sep}|#{re_sep}$/
85
+ end
86
+ # No more than one of the kebab in a row.
87
+ string.gsub!(re_duplicate_kebab, kebab)
88
+ # Remove leading/trailing kebab.
89
+ string.gsub!(re_leading_trailing_kebab, "".freeze)
90
+ end
91
+
92
+ string.downcase!
93
+ string
94
+ end
95
+ end
96
+ end
97
+ end
@@ -12,9 +12,9 @@ module JekyllImport
12
12
  end
13
13
 
14
14
  def self.specify_options(c)
15
- c.option "source", "--source NAME", "The PluXML data directory to import"
16
- c.option "layout", "--layout NAME", "The layout to apply"
17
- c.option "avoid_liquid", "--avoid_liquid true", "Will add render_with_liquid: false in frontmatter"
15
+ c.option "source", "--source NAME", "The PluXml data directory to import."
16
+ c.option "layout", "--layout NAME", "The layout to apply. (default: 'post')"
17
+ c.option "avoid_liquid", "--avoid_liquid", "Will add `render_with_liquid: false` in front matter. (default: false)"
18
18
  end
19
19
 
20
20
  def self.validate(options)
@@ -14,19 +14,19 @@ module JekyllImport
14
14
  end
15
15
 
16
16
  def self.specify_options(c)
17
- c.option "dbname", "--dbname DB", "Database name (default: '')"
18
- c.option "socket", "--socket SOCKET", "Database socket (default: '')"
19
- c.option "user", "--user USER", "Database user name (default: '')"
20
- c.option "password", "--password PW", "Database user's password (default: '')"
21
- c.option "host", "--host HOST", "Database host name (default: 'localhost')"
22
- c.option "port", "--port PORT", "Database port number (default: '3306')"
23
- c.option "clean_entities", "--clean_entities", "Whether to clean entities (default: true)"
24
- c.option "comments", "--comments", "Whether to import comments (default: true)"
25
- c.option "categories", "--categories", "Whether to import categories (default: true)"
26
- c.option "tags", "--tags", "Whether to import tags (default: true)"
17
+ c.option "dbname", "--dbname DB", "Database name."
18
+ c.option "user", "--user USER", "Database user name."
19
+ c.option "password", "--password PW", "Database user's password."
20
+ c.option "socket", "--socket SOCKET", "Database socket. (default: null)"
21
+ c.option "host", "--host HOST", "Database host name. (default: 'localhost')"
22
+ c.option "port", "--port PORT", "Database port number. (default: '3306')"
23
+ c.option "clean_entities", "--clean_entities", "Whether to clean entities. (default: true)"
24
+ c.option "comments", "--comments", "Whether to import comments. (default: true)"
25
+ c.option "categories", "--categories", "Whether to import categories. (default: true)"
26
+ c.option "tags", "--tags", "Whether to import tags. (default: true)"
27
27
 
28
28
  c.option "status", "--status STATUS,STATUS2", Array,
29
- "Array of allowed statuses (default: ['PUBLISHED'], other options: 'DRAFT')"
29
+ "Array of allowed statuses (either ['PUBLISHED'] or ['DRAFT']). (default: ['PUBLISHED'])"
30
30
  end
31
31
 
32
32
  # Main migrator function. Call this to perform the migration.
@@ -64,7 +64,7 @@ module JekyllImport
64
64
  options = {
65
65
  :user => opts.fetch("user", ""),
66
66
  :pass => opts.fetch("password", ""),
67
- :host => opts.fetch("host", "localhost"),
67
+ :host => opts.fetch("host", "127.0.0.1"),
68
68
  :port => opts.fetch("port", "3306"),
69
69
  :socket => opts.fetch("socket", nil),
70
70
  :dbname => opts.fetch("dbname", ""),
@@ -4,13 +4,16 @@ module JekyllImport
4
4
  module Importers
5
5
  class RSS < Importer
6
6
  def self.specify_options(c)
7
- c.option "source", "--source NAME", "The RSS file or URL to import"
8
- c.option "tag", "--tag NAME", "Add a tag to posts"
9
- c.option "render_audio", "--render_audio", "Render <audio> element as necessary"
7
+ c.option "source", "--source NAME", "The RSS file or URL to import."
8
+ c.option "tag", "--tag NAME", "Add a specific tag to all posts."
9
+ c.option "extract_tags", "--extract_tags KEY", "Copies tags from the given subfield on the RSS `<item>` to front matter. (default: null)"
10
+ c.option "render_audio", "--render_audio", "Render `<audio>` element in posts for the enclosure URLs. (default: false)"
11
+ c.option "canonical_link", "--canonical_link", "Add original link as `canonical_url` to post front matter. (default: false)"
10
12
  end
11
13
 
12
14
  def self.validate(options)
13
15
  abort "Missing mandatory option --source." if options["source"].nil?
16
+ abort "Provide either --tag or --extract_tags option." if options["extract_tags"] && options["tag"]
14
17
  end
15
18
 
16
19
  def self.require_deps
@@ -33,7 +36,7 @@ module JekyllImport
33
36
  source = options.fetch("source")
34
37
 
35
38
  content = ""
36
- open(source) { |s| content = s.read }
39
+ URI.open(source) { |s| content = s.read }
37
40
  rss = ::RSS::Parser.parse(content, false)
38
41
 
39
42
  raise "There doesn't appear to be any RSS items at the source (#{source}) provided." unless rss
@@ -52,13 +55,14 @@ module JekyllImport
52
55
  post_name = Jekyll::Utils.slugify(item.title, :mode => "latin")
53
56
  name = "#{formatted_date}-#{post_name}"
54
57
  audio = render_audio && item.enclosure.url
58
+ canonical_link = options.fetch("canonical_link", false)
55
59
 
56
60
  header = {
57
- "layout" => "post",
58
- "title" => item.title,
59
- }
60
-
61
- header["tag"] = options["tag"] unless options["tag"].nil? || options["tag"].empty?
61
+ "layout" => "post",
62
+ "title" => item.title,
63
+ "canonical_url" => (canonical_link ? item.link : nil),
64
+ "tag" => get_tags(item, options),
65
+ }.compact
62
66
 
63
67
  frontmatter.each do |value|
64
68
  header[value] = item.send(value)
@@ -91,6 +95,21 @@ module JekyllImport
91
95
  f.puts output
92
96
  end
93
97
  end
98
+
99
+ def self.get_tags(item, options)
100
+ explicit_tag = options["tag"]
101
+ return explicit_tag unless explicit_tag.nil? || explicit_tag.empty?
102
+
103
+ tags_reference = options["extract_tags"]
104
+ return unless tags_reference
105
+
106
+ tags_from_feed = item.instance_variable_get("@#{tags_reference}")
107
+ return unless tags_from_feed.is_a?(Array)
108
+
109
+ tags = tags_from_feed.map { |feed_tag| feed_tag.content.downcase }
110
+ tags.empty? ? nil : tags.tap(&:uniq!)
111
+ end
112
+ private_class_method :get_tags
94
113
  end
95
114
  end
96
115
  end
@@ -17,25 +17,25 @@ module JekyllImport
17
17
  end
18
18
 
19
19
  def self.specify_options(c)
20
- c.option "dbname", "--dbname DB", "Database name (default: '')"
21
- c.option "socket", "--socket SOCKET", "Database socket (default: '')"
22
- c.option "user", "--user USER", "Database user name (default: '')"
23
- c.option "password", "--password PW", "Database user's password (default: '')"
24
- c.option "host", "--host HOST", "Database host name (default: 'localhost')"
25
- c.option "port", "--port PORT", "Custom database port connect to (default: 3306)"
26
- c.option "table_prefix", "--table_prefix PREFIX", "Table prefix name (default: 'serendipity_')"
27
- c.option "clean_entities", "--clean_entities", "Whether to clean entities (default: true)"
28
- c.option "comments", "--comments", "Whether to import comments (default: true)"
29
- c.option "categories", "--categories", "Whether to import categories (default: true)"
30
- c.option "tags", "--tags", "Whether to import tags (default: true)"
31
- c.option "drafts", "--drafts", "Whether to export drafts as well"
32
- c.option "markdown", "--markdown", "convert into markdown format (default: false)"
33
- c.option "permalinks", "--permalinks", "preserve S9Y permalinks (default: false)"
34
- c.option "excerpt_separator", "--excerpt_separator", "Demarkation for excerpts (default: '<a id=\"extended\"></a>')"
35
- c.option "includeentry", "--includeentry", "Replace macros from the includeentry plugin (default: false)"
36
- c.option "imgfig", "--imgfig", "Replace nested img and youtube divs with HTML figure tags (default: true)"
37
- c.option "linebreak", "--linebreak", "Line break processing: wp, nokogiri, ignore (default: wp)"
38
- c.option "relative", "--relative", "Convert links with this prefix to relative (default:nil)"
20
+ c.option "dbname", "--dbname DB", "Database name. (default: '')"
21
+ c.option "socket", "--socket SOCKET", "Database socket. (default: '')"
22
+ c.option "user", "--user USER", "Database user name. (default: '')"
23
+ c.option "password", "--password PW", "Database user's password. (default: '')"
24
+ c.option "host", "--host HOST", "Database host name. (default: 'localhost')"
25
+ c.option "port", "--port PORT", "Custom database port connect to. (default: 3306)"
26
+ c.option "table_prefix", "--table_prefix PREFIX", "Table prefix name. (default: 'serendipity_')"
27
+ c.option "clean_entities", "--clean_entities", "Whether to clean entities. (default: true)"
28
+ c.option "comments", "--comments", "Whether to import comments. (default: true)"
29
+ c.option "categories", "--categories", "Whether to import categories. (default: true)"
30
+ c.option "tags", "--tags", "Whether to import tags. (default: true)"
31
+ c.option "drafts", "--drafts", "Whether to export drafts as well. (default: true)"
32
+ c.option "markdown", "--markdown", "convert into markdown format. (default: false)"
33
+ c.option "permalinks", "--permalinks", "preserve S9Y permalinks. (default: false)"
34
+ c.option "excerpt_separator", "--excerpt_separator", "Demarkation for excerpts. (default: '<a id=\"extended\"></a>')"
35
+ c.option "includeentry", "--includeentry", "Replace macros from the includeentry plugin. (default: false)"
36
+ c.option "imgfig", "--imgfig", "Replace nested img and youtube divs with HTML figure tags. (default: true)"
37
+ c.option "linebreak", "--linebreak", "Line break processing: wp, nokogiri, ignore. (default: wp)"
38
+ c.option "relative", "--relative", "Convert links with this prefix to relative. (default: nil)"
39
39
  end
40
40
 
41
41
  # Main migrator function. Call this to perform the migration.
@@ -99,7 +99,7 @@ module JekyllImport
99
99
  options = {
100
100
  :user => opts.fetch("user", ""),
101
101
  :pass => opts.fetch("password", ""),
102
- :host => opts.fetch("host", "localhost"),
102
+ :host => opts.fetch("host", "127.0.0.1"),
103
103
  :port => opts.fetch("port", 3306),
104
104
  :socket => opts.fetch("socket", nil),
105
105
  :dbname => opts.fetch("dbname", ""),
@@ -27,17 +27,17 @@ module JekyllImport
27
27
  end
28
28
 
29
29
  def self.specify_options(c)
30
- c.option "dbname", "--dbname DB", "Database name"
31
- c.option "user", "--user USER", "Database user name"
32
- c.option "password", "--password PW", "Database user's password"
33
- c.option "host", "--host HOST", 'Database host name (default: "localhost")'
30
+ c.option "dbname", "--dbname DB", "Database name."
31
+ c.option "user", "--user USER", "Database user name."
32
+ c.option "password", "--password PW", "Database user's password. (default: '')"
33
+ c.option "host", "--host HOST", "Database host name. (default: 'localhost')"
34
34
  end
35
35
 
36
36
  def self.process(options)
37
37
  dbname = options.fetch("dbname")
38
38
  user = options.fetch("user")
39
39
  pass = options.fetch("password", "")
40
- host = options.fetch("host", "localhost")
40
+ host = options.fetch("host", "127.0.0.1")
41
41
 
42
42
  db = Sequel.mysql2(dbname, :user => user, :password => pass, :host => host, :encoding => "utf8")
43
43
 
@@ -19,11 +19,11 @@ module JekyllImport
19
19
  end
20
20
 
21
21
  def specify_options(c)
22
- c.option "url", "--url URL", "Tumblr URL"
23
- c.option "format", "--format FORMAT", 'Output format (default: "html")'
24
- c.option "grab_images", "--grab_images", "Whether to grab images (default: false)"
25
- c.option "add_highlights", "--add_highlights", "Whether to add highlights (default: false)"
26
- c.option "rewrite_urls", "--rewrite_urls", "Whether to rewrite URLs (default: false)"
22
+ c.option "url", "--url URL", "Tumblr URL."
23
+ c.option "format", "--format FORMAT", "Output format. (default: 'html')"
24
+ c.option "grab_images", "--grab_images", "Whether to grab images. (default: false)"
25
+ c.option "add_highlights", "--add_highlights", "Whether to add highlights. (default: false)"
26
+ c.option "rewrite_urls", "--rewrite_urls", "Whether to rewrite URLs. (default: false)"
27
27
  end
28
28
 
29
29
  def process(options)
@@ -31,11 +31,11 @@ module JekyllImport
31
31
  end
32
32
 
33
33
  def self.specify_options(c)
34
- c.option "server", "--server TYPE", 'Server type ("mysql" or "postgres")'
35
- c.option "dbname", "--dbname DB", "Database name"
36
- c.option "user", "--user USER", "Database user name"
37
- c.option "password", "--password PW", "Database user's password (default: '')"
38
- c.option "host", "--host HOST", "Database host name"
34
+ c.option "server", "--server TYPE", "Server type ('mysql' or 'postgres')."
35
+ c.option "dbname", "--dbname DB", "Database name."
36
+ c.option "user", "--user USER", "Database user name."
37
+ c.option "password", "--password PW", "Database user's password. (default: '')"
38
+ c.option "host", "--host HOST", "Database host name. (default: 'localhost')"
39
39
  end
40
40
 
41
41
  def self.process(options)
@@ -43,7 +43,7 @@ module JekyllImport
43
43
  dbname = options.fetch("dbname")
44
44
  user = options.fetch("user")
45
45
  pass = options.fetch("password", "")
46
- host = options.fetch("host", "localhost")
46
+ host = options.fetch("host", "127.0.0.1")
47
47
 
48
48
  FileUtils.mkdir_p "_posts"
49
49
  case server.intern
@@ -14,23 +14,23 @@ module JekyllImport
14
14
  end
15
15
 
16
16
  def self.specify_options(c)
17
- c.option "dbname", "--dbname DB", "Database name (default: '')"
18
- c.option "socket", "--socket SOCKET", "Database socket (default: '')"
19
- c.option "user", "--user USER", "Database user name (default: '')"
20
- c.option "password", "--password PW", "Database user's password (default: '')"
21
- c.option "host", "--host HOST", "Database host name (default: 'localhost')"
22
- c.option "port", "--port PORT", "Database port number (default: '')"
23
- c.option "table_prefix", "--table_prefix PREFIX", "Table prefix name (default: 'wp_')"
24
- c.option "site_prefix", "--site_prefix PREFIX", "Site prefix name (default: '')"
25
- c.option "clean_entities", "--clean_entities", "Whether to clean entities (default: true)"
26
- c.option "comments", "--comments", "Whether to import comments (default: true)"
27
- c.option "categories", "--categories", "Whether to import categories (default: true)"
28
- c.option "tags", "--tags", "Whether to import tags (default: true)"
29
- c.option "more_excerpt", "--more_excerpt", "Whether to use more excerpt (default: true)"
30
- c.option "more_anchor", "--more_anchor", "Whether to use more anchor (default: true)"
17
+ c.option "dbname", "--dbname DB", "Database name. (default: '')"
18
+ c.option "socket", "--socket SOCKET", "Database socket. (default: '')"
19
+ c.option "user", "--user USER", "Database user name. (default: '')"
20
+ c.option "password", "--password PW", "Database user's password. (default: '')"
21
+ c.option "host", "--host HOST", "Database host name. (default: 'localhost')"
22
+ c.option "port", "--port PORT", "Database port number. (default: '')"
23
+ c.option "table_prefix", "--table_prefix PREFIX", "Table prefix name. (default: 'wp_')"
24
+ c.option "site_prefix", "--site_prefix PREFIX", "Site prefix name. (default: '')"
25
+ c.option "clean_entities", "--clean_entities", "Whether to clean entities. (default: true)"
26
+ c.option "comments", "--comments", "Whether to import comments. (default: true)"
27
+ c.option "categories", "--categories", "Whether to import categories. (default: true)"
28
+ c.option "tags", "--tags", "Whether to import tags. (default: true)"
29
+ c.option "more_excerpt", "--more_excerpt", "Whether to use more excerpt. (default: true)"
30
+ c.option "more_anchor", "--more_anchor", "Whether to use more anchor. (default: true)"
31
31
 
32
32
  c.option "status", "--status STATUS,STATUS2", Array,
33
- "Array of allowed statuses (default: ['publish'], other options: 'draft', 'private', 'revision')"
33
+ "Array of allowed statuses ('publish', 'draft', 'private', 'revision'). (default: ['publish'])"
34
34
  end
35
35
 
36
36
  # Main migrator function. Call this to perform the migration.
@@ -81,7 +81,7 @@ module JekyllImport
81
81
  options = {
82
82
  :user => opts.fetch("user", ""),
83
83
  :pass => opts.fetch("password", ""),
84
- :host => opts.fetch("host", "localhost"),
84
+ :host => opts.fetch("host", "127.0.0.1"),
85
85
  :port => opts.fetch("port", "3306"),
86
86
  :socket => opts.fetch("socket", nil),
87
87
  :dbname => opts.fetch("dbname", ""),
@@ -16,9 +16,9 @@ module JekyllImport
16
16
  end
17
17
 
18
18
  def self.specify_options(c)
19
- c.option "source", "--source FILE", 'WordPress export XML file (default: "wordpress.xml")'
20
- c.option "no_fetch_images", "--no-fetch-images", "Do not fetch the images referenced in the posts"
21
- c.option "assets_folder", "--assets_folder FOLDER", "Folder where assets such as images will be downloaded to (default: assets)"
19
+ c.option "source", "--source FILE", "WordPress export XML file (default: 'wordpress.xml')"
20
+ c.option "no_fetch_images", "--no-fetch-images", "Do not fetch the images referenced in the posts (default: false)"
21
+ c.option "assets_folder", "--assets_folder FOLDER", "Folder where assets such as images will be downloaded to (default: 'assets')"
22
22
  end
23
23
 
24
24
  # Will modify post DOM tree
@@ -28,7 +28,7 @@ module JekyllImport
28
28
 
29
29
  Jekyll.logger.info "Downloading images for ", title
30
30
  images.each do |i|
31
- uri = i["src"]
31
+ uri = URI::DEFAULT_PARSER.escape(i["src"])
32
32
 
33
33
  dst = File.join(assets_folder, File.basename(uri))
34
34
  i["src"] = File.join("{{ site.baseurl }}", dst)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JekyllImport
4
- VERSION = "0.21.0"
4
+ VERSION = "0.23.0"
5
5
  end