jekyll-import 0.12.0 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/lib/jekyll-import.rb +10 -8
  3. data/lib/jekyll-import/importer.rb +1 -1
  4. data/lib/jekyll-import/importers.rb +1 -1
  5. data/lib/jekyll-import/importers/behance.rb +20 -20
  6. data/lib/jekyll-import/importers/blogger.rb +108 -118
  7. data/lib/jekyll-import/importers/csv.rb +7 -7
  8. data/lib/jekyll-import/importers/drupal6.rb +5 -6
  9. data/lib/jekyll-import/importers/drupal7.rb +7 -13
  10. data/lib/jekyll-import/importers/drupal_common.rb +57 -59
  11. data/lib/jekyll-import/importers/easyblog.rb +30 -30
  12. data/lib/jekyll-import/importers/enki.rb +28 -29
  13. data/lib/jekyll-import/importers/ghost.rb +46 -33
  14. data/lib/jekyll-import/importers/google_reader.rb +9 -9
  15. data/lib/jekyll-import/importers/joomla.rb +32 -32
  16. data/lib/jekyll-import/importers/joomla3.rb +41 -39
  17. data/lib/jekyll-import/importers/jrnl.rb +16 -17
  18. data/lib/jekyll-import/importers/marley.rb +25 -26
  19. data/lib/jekyll-import/importers/mephisto.rb +26 -26
  20. data/lib/jekyll-import/importers/mt.rb +76 -75
  21. data/lib/jekyll-import/importers/posterous.rb +30 -29
  22. data/lib/jekyll-import/importers/rss.rb +13 -10
  23. data/lib/jekyll-import/importers/s9y.rb +16 -17
  24. data/lib/jekyll-import/importers/s9y_database.rb +98 -89
  25. data/lib/jekyll-import/importers/textpattern.rb +18 -17
  26. data/lib/jekyll-import/importers/tmp.rb +0 -0
  27. data/lib/jekyll-import/importers/tumblr.rb +146 -143
  28. data/lib/jekyll-import/importers/typo.rb +31 -31
  29. data/lib/jekyll-import/importers/wordpress.rb +100 -100
  30. data/lib/jekyll-import/importers/wordpressdotcom.rb +70 -60
  31. data/lib/jekyll-import/util.rb +24 -24
  32. data/lib/jekyll-import/version.rb +1 -1
  33. data/lib/jekyll/commands/import.rb +32 -35
  34. metadata +14 -13
@@ -2,59 +2,58 @@ module JekyllImport
2
2
  module Importers
3
3
  class Marley < Importer
4
4
  def self.validate(options)
5
- if options['marley_data_dir'].nil?
5
+ if options["marley_data_dir"].nil?
6
6
  Jekyll.logger.abort_with "Missing mandatory option --marley_data_dir."
7
7
  else
8
- unless File.directory?(options['marley_data_dir'])
9
- raise ArgumentError, "marley dir '#{options['marley_data_dir']}' not found"
8
+ unless File.directory?(options["marley_data_dir"])
9
+ raise ArgumentError, "marley dir '#{options["marley_data_dir"]}' not found"
10
10
  end
11
11
  end
12
12
  end
13
13
 
14
14
  def self.regexp
15
- { :id => /^\d{0,4}-{0,1}(.*)$/,
16
- :title => /^#\s*(.*)\s+$/,
17
- :title_with_date => /^#\s*(.*)\s+\(([0-9\/]+)\)$/,
18
- :published_on => /.*\s+\(([0-9\/]+)\)$/,
19
- :perex => /^([^\#\n]+\n)$/,
20
- :meta => /^\{\{\n(.*)\}\}\n$/mi # Multiline Regexp
21
- }
15
+ { :id => %r!^\d{0,4}-{0,1}(.*)$!,
16
+ :title => %r!^#\s*(.*)\s+$!,
17
+ :title_with_date => %r!^#\s*(.*)\s+\(([0-9\/]+)\)$!,
18
+ :published_on => %r!.*\s+\(([0-9\/]+)\)$!,
19
+ :perex => %r!^([^\#\n]+\n)$!,
20
+ :meta => %r!^\{\{\n(.*)\}\}\n$!mi, } # Multiline Regexp
22
21
  end
23
22
 
24
23
  def self.require_deps
25
- JekyllImport.require_with_fallback(%w[
24
+ JekyllImport.require_with_fallback(%w(
26
25
  fileutils
27
26
  safe_yaml
28
- ])
27
+ ))
29
28
  end
30
29
 
31
30
  def self.specify_options(c)
32
- c.option 'marley_data_dir', '--marley_data_dir DIR', 'The dir containing your marley data'
31
+ c.option "marley_data_dir", "--marley_data_dir DIR", "The dir containing your marley data"
33
32
  end
34
33
 
35
34
  def self.process(options)
36
- marley_data_dir = options.fetch('marley_data_dir')
35
+ marley_data_dir = options.fetch("marley_data_dir")
37
36
 
38
37
  FileUtils.mkdir_p "_posts"
39
38
 
40
39
  posts = 0
41
40
  Dir["#{marley_data_dir}/**/*.txt"].each do |f|
42
- next unless File.exists?(f)
41
+ next unless File.exist?(f)
43
42
 
44
- #copied over from marley's app/lib/post.rb
43
+ # copied over from marley's app/lib/post.rb
45
44
  file_content = File.read(f)
46
- meta_content = file_content.slice!( self.regexp[:meta] )
47
- body = file_content.sub( self.regexp[:title], '').sub( self.regexp[:perex], '').strip
45
+ meta_content = file_content.slice!(self.regexp[:meta])
46
+ body = file_content.sub(self.regexp[:title], "").sub(self.regexp[:perex], "").strip
48
47
 
49
- title = file_content.scan( self.regexp[:title] ).first.to_s.strip
50
- prerex = file_content.scan( self.regexp[:perex] ).first.to_s.strip
51
- published_on = DateTime.parse( post[:published_on] ) rescue File.mtime( File.dirname(f) )
52
- meta = ( meta_content ) ? YAML::load( meta_content.scan( self.regexp[:meta]).to_s ) : {}
53
- meta['title'] = title
54
- meta['layout'] = 'post'
48
+ title = file_content.scan(self.regexp[:title]).first.to_s.strip
49
+ prerex = file_content.scan(self.regexp[:perex]).first.to_s.strip
50
+ published_on = DateTime.parse(post[:published_on]) rescue File.mtime(File.dirname(f))
51
+ meta = meta_content ? YAML.safe_load(meta_content.scan(self.regexp[:meta]).to_s) : {}
52
+ meta["title"] = title
53
+ meta["layout"] = "post"
55
54
 
56
- formatted_date = published_on.strftime('%Y-%m-%d')
57
- post_name = File.dirname(f).split(%r{/}).last.gsub(/\A\d+-/, '')
55
+ formatted_date = published_on.strftime("%Y-%m-%d")
56
+ post_name = File.dirname(f).split(%r!/!).last.gsub(%r!\A\d+-!, "")
58
57
 
59
58
  name = "#{formatted_date}-#{post_name}"
60
59
  File.open("_posts/#{name}.markdown", "w") do |f|
@@ -1,8 +1,8 @@
1
1
  module JekyllImport
2
2
  module Importers
3
3
  class Mephisto < Importer
4
- #Accepts a hash with database config variables, exports mephisto posts into a csv
5
- #export PGPASSWORD if you must
4
+ # Accepts a hash with database config variables, exports mephisto posts into a csv
5
+ # export PGPASSWORD if you must
6
6
  def self.postgres(c)
7
7
  sql = <<-SQL
8
8
  BEGIN;
@@ -12,14 +12,14 @@ module JekyllImport
12
12
  COPY jekyll TO STDOUT WITH CSV HEADER;
13
13
  ROLLBACK;
14
14
  SQL
15
- command = %Q(psql -h #{c[:host] || "localhost"} -c "#{sql.strip}" #{c[:database]} #{c[:username]} -o #{c[:filename] || "posts.csv"})
15
+ command = %(psql -h #{c[:host] || "localhost"} -c "#{sql.strip}" #{c[:database]} #{c[:username]} -o #{c[:filename] || "posts.csv"})
16
16
  puts command
17
17
  `#{command}`
18
18
  CSV.process
19
19
  end
20
20
 
21
21
  def self.validate(options)
22
- %w[dbname user].each do |option|
22
+ %w(dbname user).each do |option|
23
23
  if options[option].nil?
24
24
  abort "Missing mandatory option --#{option}."
25
25
  end
@@ -27,19 +27,20 @@ module JekyllImport
27
27
  end
28
28
 
29
29
  def self.require_deps
30
- JekyllImport.require_with_fallback(%w[
30
+ JekyllImport.require_with_fallback(%w(
31
31
  rubygems
32
32
  sequel
33
+ mysql2
33
34
  fastercsv
34
35
  fileutils
35
- ])
36
+ ))
36
37
  end
37
38
 
38
39
  def self.specify_options(c)
39
- c.option 'dbname', '--dbname DB', 'Database name'
40
- c.option 'user', '--user USER', 'Database user name'
41
- c.option 'password', '--password PW', "Database user's password (default: '')"
42
- c.option 'host', '--host HOST', 'Database host name (default: "localhost")'
40
+ c.option "dbname", "--dbname DB", "Database name"
41
+ c.option "user", "--user USER", "Database user name"
42
+ c.option "password", "--password PW", "Database user's password (default: '')"
43
+ c.option "host", "--host HOST", 'Database host name (default: "localhost")'
43
44
  end
44
45
 
45
46
  # This query will pull blog posts from all entries across all blogs. If
@@ -54,18 +55,18 @@ module JekyllImport
54
55
  WHERE user_id = 1 AND \
55
56
  type = 'Article' AND \
56
57
  published_at IS NOT NULL \
57
- ORDER BY published_at"
58
+ ORDER BY published_at".freeze
58
59
 
59
60
  def self.process(options)
60
- dbname = options.fetch('dbname')
61
- user = options.fetch('user')
62
- pass = options.fetch('password', '')
63
- host = options.fetch('host', "localhost")
61
+ dbname = options.fetch("dbname")
62
+ user = options.fetch("user")
63
+ pass = options.fetch("password", "")
64
+ host = options.fetch("host", "localhost")
64
65
 
65
- db = Sequel.mysql(dbname, :user => user,
66
- :password => pass,
67
- :host => host,
68
- :encoding => 'utf8')
66
+ db = Sequel.mysql2(dbname, :user => user,
67
+ :password => pass,
68
+ :host => host,
69
+ :encoding => "utf8")
69
70
 
70
71
  FileUtils.mkdir_p "_posts"
71
72
 
@@ -78,13 +79,13 @@ module JekyllImport
78
79
  # Ideally, this script would determine the post format (markdown,
79
80
  # html, etc) and create files with proper extensions. At this point
80
81
  # it just assumes that markdown will be acceptable.
81
- name = [date.year, date.month, date.day, slug].join('-') + ".markdown"
82
+ name = [date.year, date.month, date.day, slug].join("-") + ".markdown"
82
83
 
83
84
  data = {
84
- 'layout' => 'post',
85
- 'title' => title.to_s,
86
- 'mt_id' => post[:entry_id],
87
- }.delete_if { |k,v| v.nil? || v == ''}.to_yaml
85
+ "layout" => "post",
86
+ "title" => title.to_s,
87
+ "mt_id" => post[:entry_id],
88
+ }.delete_if { |_k, v| v.nil? || v == "" }.to_yaml
88
89
 
89
90
  File.open("_posts/#{name}", "w") do |f|
90
91
  f.puts data
@@ -92,8 +93,7 @@ module JekyllImport
92
93
  f.puts content
93
94
  end
94
95
  end
95
-
96
96
  end
97
97
  end
98
98
  end
99
- end
99
+ end
@@ -1,44 +1,46 @@
1
1
  module JekyllImport
2
2
  module Importers
3
3
  class MT < Importer
4
-
5
- SUPPORTED_ENGINES = %{mysql postgres sqlite}
4
+ SUPPORTED_ENGINES = %(mysql postgres sqlite).freeze
6
5
 
7
6
  STATUS_DRAFT = 1
8
7
  STATUS_PUBLISHED = 2
9
- MORE_CONTENT_SEPARATOR = '<!--more-->'
8
+ MORE_CONTENT_SEPARATOR = "<!--more-->".freeze
10
9
 
11
10
  def self.default_options
12
11
  {
13
- 'blog_id' => nil,
14
- 'categories' => true,
15
- 'dest_encoding' => 'utf-8',
16
- 'src_encoding' => 'utf-8',
17
- 'comments' => false
12
+ "blog_id" => nil,
13
+ "categories" => true,
14
+ "dest_encoding" => "utf-8",
15
+ "src_encoding" => "utf-8",
16
+ "comments" => false,
18
17
  }
19
18
  end
20
19
 
21
20
  def self.require_deps
22
- JekyllImport.require_with_fallback(%w[
21
+ JekyllImport.require_with_fallback(%w(
23
22
  rubygems
24
23
  sequel
24
+ sqlite3
25
+ mysql2
26
+ pg
25
27
  fileutils
26
28
  safe_yaml
27
- ])
29
+ ))
28
30
  end
29
31
 
30
32
  def self.specify_options(c)
31
- c.option 'engine', "--engine ENGINE", "Database engine, (default: 'mysql', postgres also supported)"
32
- c.option 'dbname', '--dbname DB', 'Database name'
33
- c.option 'user', '--user USER', 'Database user name'
34
- c.option 'password', '--password PW', "Database user's password, (default: '')"
35
- c.option 'host', '--host HOST', 'Database host name (default: "localhost")'
36
- c.option 'port', '--port PORT', 'Custom database port connect to (optional)'
37
- c.option 'blog_id', '--blog_id ID', 'Specify a single Movable Type blog ID to import (default: all blogs)'
38
- c.option 'categories', '--categories', "If true, save post's categories in its YAML front matter. (default: true)"
39
- c.option 'src_encoding', '--src_encoding ENCODING', "Encoding of strings from database. (default: UTF-8)"
40
- c.option 'dest_encoding', '--dest_encoding ENCODING', "Encoding of output strings. (default: UTF-8)"
41
- c.option 'comments','--comments', "If true, output comments in _comments directory (default: false)"
33
+ c.option "engine", "--engine ENGINE", "Database engine, (default: 'mysql', postgres also supported)"
34
+ c.option "dbname", "--dbname DB", "Database name"
35
+ c.option "user", "--user USER", "Database user name"
36
+ c.option "password", "--password PW", "Database user's password, (default: '')"
37
+ c.option "host", "--host HOST", 'Database host name (default: "localhost")'
38
+ c.option "port", "--port PORT", "Custom database port connect to (optional)"
39
+ c.option "blog_id", "--blog_id ID", "Specify a single Movable Type blog ID to import (default: all blogs)"
40
+ c.option "categories", "--categories", "If true, save post's categories in its YAML front matter. (default: true)"
41
+ c.option "src_encoding", "--src_encoding ENCODING", "Encoding of strings from database. (default: UTF-8)"
42
+ c.option "dest_encoding", "--dest_encoding ENCODING", "Encoding of output strings. (default: UTF-8)"
43
+ c.option "comments", "--comments", "If true, output comments in _comments directory (default: false)"
42
44
  end
43
45
 
44
46
  # By default this migrator will include posts for all your MovableType blogs.
@@ -67,7 +69,7 @@ module JekyllImport
67
69
  def self.process(options)
68
70
  options = default_options.merge(options)
69
71
 
70
- comments = options.fetch('comments')
72
+ comments = options.fetch("comments")
71
73
  posts_name_by_id = {} if comments
72
74
 
73
75
  db = database_from_opts(options)
@@ -77,20 +79,20 @@ module JekyllImport
77
79
  FileUtils.mkdir_p "_posts"
78
80
 
79
81
  posts = db[:mt_entry]
80
- posts = posts.filter(:entry_blog_id => options['blog_id']) if options['blog_id']
82
+ posts = posts.filter(:entry_blog_id => options["blog_id"]) if options["blog_id"]
81
83
  posts.each do |post|
82
84
  categories = post_categories.filter(
83
85
  :mt_placement__placement_entry_id => post[:entry_id]
84
- ).map {|ea| encode(ea[:category_basename], options) }
86
+ ).map { |ea| encode(ea[:category_basename], options) }
85
87
 
86
88
  file_name = post_file_name(post, options)
87
89
 
88
90
  data = post_metadata(post, options)
89
- data['categories'] = categories if !categories.empty? && options['categories']
90
- yaml_front_matter = data.delete_if { |_,v| v.nil? || v == '' }.to_yaml
91
+ data["categories"] = categories if !categories.empty? && options["categories"]
92
+ yaml_front_matter = data.delete_if { |_, v| v.nil? || v == "" }.to_yaml
91
93
 
92
94
  # save post path for comment processing
93
- posts_name_by_id[data['post_id']] = file_name if comments
95
+ posts_name_by_id[data["post_id"]] = file_name if comments
94
96
 
95
97
  content = post_content(post, options)
96
98
 
@@ -107,38 +109,36 @@ module JekyllImport
107
109
 
108
110
  comments = db[:mt_comment]
109
111
  comments.each do |comment|
110
- if posts_name_by_id.key?(comment[:comment_entry_id]) # if the entry exists
111
- dir_name, base_name = comment_file_dir_and_base_name(posts_name_by_id, comment, options)
112
- FileUtils.mkdir_p "_comments/#{dir_name}"
113
-
114
- data = comment_metadata(comment, options)
115
- content = comment_content(comment, options)
116
- yaml_front_matter = data.delete_if { |_,v| v.nil? || v == '' }.to_yaml
117
-
118
- File.open("_comments/#{dir_name}/#{base_name}", "w") do |f|
119
- f.puts yaml_front_matter
120
- f.puts "---"
121
- f.puts encode(content, options)
122
- end
112
+ next unless posts_name_by_id.key?(comment[:comment_entry_id]) # if the entry exists
113
+ dir_name, base_name = comment_file_dir_and_base_name(posts_name_by_id, comment, options)
114
+ FileUtils.mkdir_p "_comments/#{dir_name}"
115
+
116
+ data = comment_metadata(comment, options)
117
+ content = comment_content(comment, options)
118
+ yaml_front_matter = data.delete_if { |_, v| v.nil? || v == "" }.to_yaml
119
+
120
+ File.open("_comments/#{dir_name}/#{base_name}", "w") do |f|
121
+ f.puts yaml_front_matter
122
+ f.puts "---"
123
+ f.puts encode(content, options)
123
124
  end
124
125
  end
125
126
  end
126
-
127
127
  end
128
128
 
129
129
  # Extracts metadata for YAML front matter from post
130
130
  def self.post_metadata(post, options = default_options)
131
131
  metadata = {
132
- 'layout' => 'post',
133
- 'title' => encode(post[:entry_title], options),
134
- 'date' => post_date(post).strftime("%Y-%m-%d %H:%M:%S %z"),
135
- 'excerpt' => encode(post[:entry_excerpt].to_s, options),
136
- 'mt_id' => post[:entry_id],
137
- 'blog_id' => post[:entry_blog_id],
138
- 'post_id' => post[:entry_id], # for link with comments
139
- 'basename' => post[:entry_basename]
132
+ "layout" => "post",
133
+ "title" => encode(post[:entry_title], options),
134
+ "date" => post_date(post).strftime("%Y-%m-%d %H:%M:%S %z"),
135
+ "excerpt" => encode(post[:entry_excerpt].to_s, options),
136
+ "mt_id" => post[:entry_id],
137
+ "blog_id" => post[:entry_blog_id],
138
+ "post_id" => post[:entry_id], # for link with comments
139
+ "basename" => post[:entry_basename],
140
140
  }
141
- metadata['published'] = false if post[:entry_status] != STATUS_PUBLISHED
141
+ metadata["published"] = false if post[:entry_status] != STATUS_PUBLISHED
142
142
  metadata
143
143
  end
144
144
 
@@ -152,7 +152,7 @@ module JekyllImport
152
152
  post[:entry_text_more].nil? || post[:entry_text_more].strip.empty?
153
153
  end
154
154
 
155
- def self.post_content(post, options = default_options)
155
+ def self.post_content(post, _options = default_options)
156
156
  if extra_entry_text_empty?(post)
157
157
  post[:entry_text]
158
158
  else
@@ -160,27 +160,27 @@ module JekyllImport
160
160
  end
161
161
  end
162
162
 
163
- def self.post_file_name(post, options = default_options)
163
+ def self.post_file_name(post, _options = default_options)
164
164
  date = post_date(post)
165
165
  slug = post[:entry_basename]
166
166
  file_ext = suffix(post[:entry_convert_breaks])
167
167
 
168
- "#{date.strftime('%Y-%m-%d')}-#{slug}.#{file_ext}"
168
+ "#{date.strftime("%Y-%m-%d")}-#{slug}.#{file_ext}"
169
169
  end
170
170
 
171
171
  # Extracts metadata for YAML front matter from comment
172
172
  def self.comment_metadata(comment, options = default_options)
173
173
  metadata = {
174
- 'layout' => 'comment',
175
- 'comment_id' => comment[:comment_id],
176
- 'post_id' => comment[:comment_entry_id],
177
- 'author' => encode(comment[:comment_author], options),
178
- 'email' => comment[:comment_email],
179
- 'commenter_id' => comment[:comment_commenter_id],
180
- 'date' => comment_date(comment).strftime("%Y-%m-%d %H:%M:%S %z"),
181
- 'visible' => comment[:comment_visible] == 1,
182
- 'ip' => comment[:comment_ip],
183
- 'url' => comment[:comment_url]
174
+ "layout" => "comment",
175
+ "comment_id" => comment[:comment_id],
176
+ "post_id" => comment[:comment_entry_id],
177
+ "author" => encode(comment[:comment_author], options),
178
+ "email" => comment[:comment_email],
179
+ "commenter_id" => comment[:comment_commenter_id],
180
+ "date" => comment_date(comment).strftime("%Y-%m-%d %H:%M:%S %z"),
181
+ "visible" => comment[:comment_visible] == 1,
182
+ "ip" => comment[:comment_ip],
183
+ "url" => comment[:comment_url],
184
184
  }
185
185
  metadata
186
186
  end
@@ -190,12 +190,12 @@ module JekyllImport
190
190
  comment[:comment_modified_on] || comment[:comment_created_on]
191
191
  end
192
192
 
193
- def self.comment_content(comment, options = default_options)
193
+ def self.comment_content(comment, _options = default_options)
194
194
  comment[:comment_text]
195
195
  end
196
196
 
197
- def self.comment_file_dir_and_base_name(posts_name_by_id, comment, options = default_options)
198
- post_basename = posts_name_by_id[comment[:comment_entry_id]].sub(/\.\w+$/, '')
197
+ def self.comment_file_dir_and_base_name(posts_name_by_id, comment, _options = default_options)
198
+ post_basename = posts_name_by_id[comment[:comment_entry_id]].sub(%r!\.\w+$!, "")
199
199
  comment_id = comment[:comment_id]
200
200
 
201
201
  [post_basename, "#{comment_id}.markdown"]
@@ -203,7 +203,7 @@ module JekyllImport
203
203
 
204
204
  def self.encode(str, options = default_options)
205
205
  if str.respond_to?(:encoding)
206
- str.encode(options['dest_encoding'], options['src_encoding'])
206
+ str.encode(options["dest_encoding"], options["src_encoding"])
207
207
  else
208
208
  str
209
209
  end
@@ -230,26 +230,27 @@ module JekyllImport
230
230
  end
231
231
 
232
232
  def self.database_from_opts(options)
233
- engine = options.fetch('engine', 'mysql')
234
- dbname = options.fetch('dbname')
233
+ engine = options.fetch("engine", "mysql")
234
+ dbname = options.fetch("dbname")
235
+ sequel_engine = engine == "mysql" ? "mysql2" : engine
235
236
 
236
237
  case engine
237
238
  when "sqlite"
238
239
  Sequel.sqlite(dbname)
239
240
  when "mysql", "postgres"
240
241
  db_connect_opts = {
241
- :host => options.fetch('host', 'localhost'),
242
- :user => options.fetch('user'),
243
- :password => options.fetch('password', '')
242
+ :host => options.fetch("host", "localhost"),
243
+ :user => options.fetch("user"),
244
+ :password => options.fetch("password", ""),
244
245
  }
245
- db_connect_opts = options['port'] if options['port']
246
+ db_connect_opts = options["port"] if options["port"]
246
247
  Sequel.public_send(
247
- engine,
248
+ sequel_engine,
248
249
  dbname,
249
250
  db_connect_opts
250
251
  )
251
252
  else
252
- abort("Unsupported engine: '#{engine}'. Must be one of #{SUPPORTED_ENGINES.join(', ')}")
253
+ abort("Unsupported engine: '#{engine}'. Must be one of #{SUPPORTED_ENGINES.join(", ")}")
253
254
  end
254
255
  end
255
256
  end