embulk 0.6.16 → 0.6.17
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 +4 -4
- data/README.md +33 -45
- data/build.gradle +3 -3
- data/embulk-core/src/main/java/org/embulk/spi/Exec.java +6 -0
- data/embulk-core/src/main/java/org/embulk/spi/util/InputStreamFileInput.java +73 -1
- data/embulk-core/src/main/java/org/embulk/spi/util/InputStreamTransactionalFileInput.java +25 -0
- data/embulk-core/src/main/java/org/embulk/spi/util/Timestamps.java +53 -0
- data/embulk-docs/src/_static/embulk-logo.svg +133 -0
- data/embulk-docs/src/release.rst +1 -0
- data/embulk-docs/src/release/release-0.6.17.rst +39 -0
- data/embulk-standards/src/main/java/org/embulk/standards/CsvFormatterPlugin.java +2 -17
- data/embulk-standards/src/main/java/org/embulk/standards/CsvParserPlugin.java +3 -22
- data/embulk-standards/src/main/java/org/embulk/standards/GzipFileDecoderPlugin.java +2 -2
- data/embulk-standards/src/main/java/org/embulk/standards/GzipFileEncoderPlugin.java +0 -1
- data/embulk-standards/src/main/java/org/embulk/standards/LocalFileInputPlugin.java +18 -42
- data/embulk-standards/src/main/java/org/embulk/standards/LocalFileOutputPlugin.java +3 -3
- data/lib/embulk/command/embulk_new_plugin.rb +40 -14
- data/lib/embulk/command/embulk_run.rb +3 -3
- data/lib/embulk/data/new/README.md.erb +18 -17
- data/lib/embulk/data/new/java/build.gradle.erb +1 -1
- data/lib/embulk/data/new/java/decoder.java.erb +53 -9
- data/lib/embulk/data/new/java/encoder.java.erb +54 -8
- data/lib/embulk/data/new/java/file_input.java.erb +91 -12
- data/lib/embulk/data/new/java/file_output.java.erb +35 -8
- data/lib/embulk/data/new/java/filter.java.erb +16 -7
- data/lib/embulk/data/new/java/formatter.java.erb +16 -7
- data/lib/embulk/data/new/java/input.java.erb +18 -14
- data/lib/embulk/data/new/java/output.java.erb +16 -8
- data/lib/embulk/data/new/java/parser.java.erb +17 -8
- data/lib/embulk/data/new/java/plugin_loader.rb.erb +1 -1
- data/lib/embulk/data/new/java/test.java.erb +1 -1
- data/lib/embulk/data/new/ruby/filter.rb.erb +6 -4
- data/lib/embulk/data/new/ruby/formatter.rb.erb +6 -4
- data/lib/embulk/data/new/ruby/gemspec.erb +2 -2
- data/lib/embulk/data/new/ruby/input.rb.erb +6 -4
- data/lib/embulk/data/new/ruby/output.rb.erb +6 -4
- data/lib/embulk/data/new/ruby/parser.rb.erb +6 -4
- data/lib/embulk/file_input.rb +4 -0
- data/lib/embulk/file_output.rb +4 -0
- data/lib/embulk/version.rb +1 -1
- metadata +8 -4
data/embulk-docs/src/release.rst
CHANGED
@@ -0,0 +1,39 @@
|
|
1
|
+
Release 0.6.17
|
2
|
+
==================================
|
3
|
+
|
4
|
+
General Changes
|
5
|
+
------------------
|
6
|
+
|
7
|
+
* '-' in generated plugin name is replaed to '_'.
|
8
|
+
|
9
|
+
* Package name of generated java plugins changed from org.embulk.<category> to org.embulk.<category>.<plugin name>.
|
10
|
+
|
11
|
+
* Generated plugin templates use recommended utility classes by default.
|
12
|
+
|
13
|
+
* java-decode template generator uses InputStreamFileInput.
|
14
|
+
|
15
|
+
* java-encoder template generator uses FileOutputOutputStream.
|
16
|
+
|
17
|
+
* java-file-input template generator uses InputStreamTransactionalFileInput.
|
18
|
+
|
19
|
+
* guess method of generated java-input plugin returns empty config diff rather throwing an exception.
|
20
|
+
|
21
|
+
|
22
|
+
Java Plugin API
|
23
|
+
------------------
|
24
|
+
|
25
|
+
* Added ``spi.Exec.getTransactionTime()``.
|
26
|
+
* Added ``spi.util.Timestamps`` utility class. This method is useful to create ``TimestampParser`` and ``TimestampFormatter`` which are configurable by users.
|
27
|
+
* Added ``spi.util.InputStreamFileInput.Opener`` interface to open single file.
|
28
|
+
* Added ``spi.util.InputStreamFileInput()`` with ``InputStream`` to use a pre-opend stream.
|
29
|
+
* Added ``spi.util.InputStreamTransactionalFileInput`` for convenience of ``FileInputPlugin``.
|
30
|
+
|
31
|
+
Ruby Plugin API
|
32
|
+
------------------
|
33
|
+
|
34
|
+
* Added ``FileInput#to_java`` and ``FileOutput#to_java``.
|
35
|
+
|
36
|
+
|
37
|
+
Release Date
|
38
|
+
------------------
|
39
|
+
2015-07-17
|
@@ -21,6 +21,7 @@ import org.embulk.spi.PageReader;
|
|
21
21
|
import org.embulk.spi.Exec;
|
22
22
|
import org.embulk.spi.FileOutput;
|
23
23
|
import org.embulk.spi.util.LineEncoder;
|
24
|
+
import org.embulk.spi.util.Timestamps;
|
24
25
|
|
25
26
|
import org.embulk.spi.util.Newline;
|
26
27
|
import java.util.Map;
|
@@ -101,29 +102,13 @@ public class CsvFormatterPlugin
|
|
101
102
|
control.run(task.dump());
|
102
103
|
}
|
103
104
|
|
104
|
-
private TimestampFormatter[] newTimestampFormatters(
|
105
|
-
TimestampFormatter.Task formatterTask, Schema schema,
|
106
|
-
Map<String, TimestampColumnOption> columnOptions)
|
107
|
-
{
|
108
|
-
TimestampFormatter[] formatters = new TimestampFormatter[schema.getColumnCount()];
|
109
|
-
int i = 0;
|
110
|
-
for (Column column : schema.getColumns()) {
|
111
|
-
if (column.getType() instanceof TimestampType) {
|
112
|
-
Optional<TimestampColumnOption> option = Optional.fromNullable(columnOptions.get(column.getName()));
|
113
|
-
formatters[i] = new TimestampFormatter(formatterTask, option);
|
114
|
-
}
|
115
|
-
i++;
|
116
|
-
}
|
117
|
-
return formatters;
|
118
|
-
}
|
119
|
-
|
120
105
|
@Override
|
121
106
|
public PageOutput open(TaskSource taskSource, final Schema schema,
|
122
107
|
FileOutput output)
|
123
108
|
{
|
124
109
|
final PluginTask task = taskSource.loadTask(PluginTask.class);
|
125
110
|
final LineEncoder encoder = new LineEncoder(output, task);
|
126
|
-
final TimestampFormatter[] timestampFormatters =
|
111
|
+
final TimestampFormatter[] timestampFormatters = Timestamps.newTimestampColumnFormatters(task, schema, task.getColumnOptions());
|
127
112
|
final char delimiter = task.getDelimiterChar();
|
128
113
|
final QuotePolicy quotePolicy = task.getQuotePolicy();
|
129
114
|
final char quote = task.getQuoteChar() != '\0' ? task.getQuoteChar() : '"';
|
@@ -9,7 +9,6 @@ import org.embulk.config.ConfigSource;
|
|
9
9
|
import org.embulk.config.ConfigException;
|
10
10
|
import org.embulk.config.TaskSource;
|
11
11
|
import org.embulk.spi.type.TimestampType;
|
12
|
-
import org.embulk.spi.time.TimestampFormat;
|
13
12
|
import org.embulk.spi.time.TimestampParser;
|
14
13
|
import org.embulk.spi.time.TimestampParseException;
|
15
14
|
import org.embulk.spi.Column;
|
@@ -23,6 +22,7 @@ import org.embulk.spi.Exec;
|
|
23
22
|
import org.embulk.spi.FileInput;
|
24
23
|
import org.embulk.spi.PageOutput;
|
25
24
|
import org.embulk.spi.util.LineDecoder;
|
25
|
+
import org.embulk.spi.util.Timestamps;
|
26
26
|
import org.slf4j.Logger;
|
27
27
|
|
28
28
|
public class CsvParserPlugin
|
@@ -90,10 +90,6 @@ public class CsvParserPlugin
|
|
90
90
|
public boolean getAllowExtraColumns();
|
91
91
|
}
|
92
92
|
|
93
|
-
public interface TimestampColumnOption
|
94
|
-
extends Task, TimestampParser.TimestampColumnOption
|
95
|
-
{ }
|
96
|
-
|
97
93
|
private final Logger log;
|
98
94
|
|
99
95
|
public CsvParserPlugin()
|
@@ -121,27 +117,12 @@ public class CsvParserPlugin
|
|
121
117
|
control.run(task.dump(), task.getSchemaConfig().toSchema());
|
122
118
|
}
|
123
119
|
|
124
|
-
private TimestampParser[] newTimestampParsers(
|
125
|
-
TimestampParser.Task parserTask, SchemaConfig schema)
|
126
|
-
{
|
127
|
-
TimestampParser[] parsers = new TimestampParser[schema.getColumnCount()];
|
128
|
-
int i = 0;
|
129
|
-
for (ColumnConfig column : schema.getColumns()) {
|
130
|
-
if (column.getType() instanceof TimestampType) {
|
131
|
-
TimestampColumnOption option = column.getOption().loadConfig(TimestampColumnOption.class);
|
132
|
-
parsers[i] = new TimestampParser(parserTask, option);
|
133
|
-
}
|
134
|
-
i++;
|
135
|
-
}
|
136
|
-
return parsers;
|
137
|
-
}
|
138
|
-
|
139
120
|
@Override
|
140
121
|
public void run(TaskSource taskSource, final Schema schema,
|
141
122
|
FileInput input, PageOutput output)
|
142
123
|
{
|
143
124
|
PluginTask task = taskSource.loadTask(PluginTask.class);
|
144
|
-
final TimestampParser[]
|
125
|
+
final TimestampParser[] timestampParsers = Timestamps.newTimestampColumnParsers(task, task.getSchemaConfig());
|
145
126
|
LineDecoder lineDecoder = new LineDecoder(input, task);
|
146
127
|
final CsvTokenizer tokenizer = new CsvTokenizer(lineDecoder, task);
|
147
128
|
final String nullStringOrNull = task.getNullString().orNull();
|
@@ -225,7 +206,7 @@ public class CsvParserPlugin
|
|
225
206
|
pageBuilder.setNull(column);
|
226
207
|
} else {
|
227
208
|
try {
|
228
|
-
pageBuilder.setTimestamp(column,
|
209
|
+
pageBuilder.setTimestamp(column, timestampParsers[column.getIndex()].parse(v));
|
229
210
|
} catch (TimestampParseException e) {
|
230
211
|
// TODO support default value
|
231
212
|
throw new CsvRecordValidateException(e);
|
@@ -31,10 +31,10 @@ public class GzipFileDecoderPlugin
|
|
31
31
|
}
|
32
32
|
|
33
33
|
@Override
|
34
|
-
public FileInput open(TaskSource taskSource, FileInput
|
34
|
+
public FileInput open(TaskSource taskSource, FileInput fileInput)
|
35
35
|
{
|
36
36
|
PluginTask task = taskSource.loadTask(PluginTask.class);
|
37
|
-
final FileInputInputStream files = new FileInputInputStream(
|
37
|
+
final FileInputInputStream files = new FileInputInputStream(fileInput);
|
38
38
|
return new InputStreamFileInput(
|
39
39
|
task.getBufferAllocator(),
|
40
40
|
new InputStreamFileInput.Provider() {
|
@@ -28,7 +28,7 @@ import org.embulk.spi.BufferAllocator;
|
|
28
28
|
import org.embulk.spi.Exec;
|
29
29
|
import org.embulk.spi.FileInputPlugin;
|
30
30
|
import org.embulk.spi.TransactionalFileInput;
|
31
|
-
import org.embulk.spi.util.
|
31
|
+
import org.embulk.spi.util.InputStreamTransactionalFileInput;
|
32
32
|
import org.slf4j.Logger;
|
33
33
|
|
34
34
|
public class LocalFileInputPlugin
|
@@ -177,52 +177,28 @@ public class LocalFileInputPlugin
|
|
177
177
|
@Override
|
178
178
|
public TransactionalFileInput open(TaskSource taskSource, int taskIndex)
|
179
179
|
{
|
180
|
-
PluginTask task = taskSource.loadTask(PluginTask.class);
|
181
|
-
return new LocalFileInput(task, taskIndex);
|
182
|
-
}
|
180
|
+
final PluginTask task = taskSource.loadTask(PluginTask.class);
|
183
181
|
|
184
|
-
|
185
|
-
extends InputStreamFileInput
|
186
|
-
implements TransactionalFileInput
|
187
|
-
{
|
188
|
-
// TODO create single-file InputStreamFileInput utility
|
189
|
-
private static class SingleFileProvider
|
190
|
-
implements InputStreamFileInput.Provider
|
191
|
-
{
|
192
|
-
private final File file;
|
193
|
-
private boolean opened = false;
|
182
|
+
final File file = new File(task.getFiles().get(taskIndex));
|
194
183
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
184
|
+
return new InputStreamTransactionalFileInput(
|
185
|
+
task.getBufferAllocator(),
|
186
|
+
new InputStreamTransactionalFileInput.Opener() {
|
187
|
+
public InputStream open() throws IOException
|
188
|
+
{
|
189
|
+
return new FileInputStream(file);
|
190
|
+
}
|
191
|
+
})
|
192
|
+
{
|
193
|
+
@Override
|
194
|
+
public void abort()
|
195
|
+
{ }
|
199
196
|
|
200
197
|
@Override
|
201
|
-
public
|
198
|
+
public CommitReport commit()
|
202
199
|
{
|
203
|
-
|
204
|
-
return null;
|
205
|
-
}
|
206
|
-
opened = true;
|
207
|
-
return new FileInputStream(file);
|
200
|
+
return Exec.newCommitReport();
|
208
201
|
}
|
209
|
-
|
210
|
-
@Override
|
211
|
-
public void close() { }
|
212
|
-
}
|
213
|
-
|
214
|
-
public LocalFileInput(PluginTask task, int taskIndex)
|
215
|
-
{
|
216
|
-
super(task.getBufferAllocator(), new SingleFileProvider(new File(task.getFiles().get(taskIndex))));
|
217
|
-
}
|
218
|
-
|
219
|
-
@Override
|
220
|
-
public void abort() { }
|
221
|
-
|
222
|
-
@Override
|
223
|
-
public CommitReport commit()
|
224
|
-
{
|
225
|
-
return Exec.newCommitReport();
|
226
|
-
}
|
202
|
+
};
|
227
203
|
}
|
228
204
|
}
|
@@ -82,9 +82,8 @@ public class LocalFileOutputPlugin
|
|
82
82
|
final String pathSuffix = task.getFileNameExtension();
|
83
83
|
final String sequenceFormat = task.getSequenceFormat();
|
84
84
|
|
85
|
-
final List<String> fileNames = new ArrayList<>();
|
86
|
-
|
87
85
|
return new TransactionalFileOutput() {
|
86
|
+
private final List<String> fileNames = new ArrayList<>();
|
88
87
|
private int fileIndex = 0;
|
89
88
|
private FileOutputStream output = null;
|
90
89
|
|
@@ -134,7 +133,8 @@ public class LocalFileOutputPlugin
|
|
134
133
|
closeFile();
|
135
134
|
}
|
136
135
|
|
137
|
-
public void abort()
|
136
|
+
public void abort()
|
137
|
+
{ }
|
138
138
|
|
139
139
|
public CommitReport commit()
|
140
140
|
{
|
@@ -8,28 +8,35 @@ module Embulk
|
|
8
8
|
embulk_category = :input if category == :file_input
|
9
9
|
embulk_category = :output if category == :file_output
|
10
10
|
|
11
|
-
|
11
|
+
name = name.gsub(/[^a-zA-Z0-9_]+/, '_') # replace '-' to '_'
|
12
|
+
|
13
|
+
full_project_name = "embulk-#{embulk_category}-#{name}"
|
12
14
|
plugin_dir = "lib/embulk"
|
13
15
|
plugin_path = "#{plugin_dir}/#{embulk_category}/#{name}.rb"
|
14
16
|
|
15
|
-
if File.exist?(
|
16
|
-
raise "./#{
|
17
|
+
if File.exist?(full_project_name)
|
18
|
+
raise "./#{full_project_name} already exists. Please delete it first."
|
17
19
|
end
|
18
|
-
FileUtils.mkdir_p(
|
20
|
+
FileUtils.mkdir_p(full_project_name)
|
19
21
|
|
20
|
-
puts "Creating #{
|
22
|
+
puts "Creating #{full_project_name}/"
|
21
23
|
|
22
24
|
success = false
|
23
25
|
begin
|
26
|
+
#
|
27
|
+
# Generate gemspec
|
28
|
+
#
|
24
29
|
author = `git config user.name`.strip
|
25
30
|
author = "YOUR_NAME" if author.empty?
|
26
31
|
email = `git config user.email`.strip
|
27
32
|
email = "YOUR_NAME" if email.empty?
|
28
33
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
34
|
+
# variables used in erb templates
|
35
|
+
ruby_class_name = name.split('_').map {|a| a.capitalize }.join
|
36
|
+
java_iface_name = category.to_s.split('_').map {|a| a.capitalize }.join
|
37
|
+
java_class_name = name.split('_').map {|a| a.capitalize }.join + java_iface_name + "Plugin"
|
38
|
+
java_package_name = "org.embulk.#{embulk_category}.#{name}"
|
39
|
+
display_name = name.split('_').map {|a| a.capitalize }.join(' ')
|
33
40
|
display_category = category.to_s.gsub('_', ' ')
|
34
41
|
|
35
42
|
extra_guess_erb = {}
|
@@ -57,7 +64,10 @@ module Embulk
|
|
57
64
|
description = %[#{display_name}]
|
58
65
|
end
|
59
66
|
|
60
|
-
|
67
|
+
#
|
68
|
+
# Generate project repository
|
69
|
+
#
|
70
|
+
pkg = Embulk::PackageData.new("new", full_project_name, binding())
|
61
71
|
|
62
72
|
pkg.cp_erb("README.md.erb", "README.md")
|
63
73
|
pkg.cp("LICENSE.txt", "LICENSE.txt")
|
@@ -67,7 +77,7 @@ module Embulk
|
|
67
77
|
when :ruby
|
68
78
|
pkg.cp("ruby/Rakefile", "Rakefile")
|
69
79
|
pkg.cp("ruby/Gemfile", "Gemfile")
|
70
|
-
pkg.cp_erb("ruby/gemspec.erb", "#{
|
80
|
+
pkg.cp_erb("ruby/gemspec.erb", "#{full_project_name}.gemspec")
|
71
81
|
pkg.cp_erb("ruby/#{category}.rb.erb", plugin_path)
|
72
82
|
|
73
83
|
when :java
|
@@ -78,18 +88,34 @@ module Embulk
|
|
78
88
|
pkg.set_executable("gradlew")
|
79
89
|
pkg.cp_erb("java/build.gradle.erb", "build.gradle")
|
80
90
|
pkg.cp_erb("java/plugin_loader.rb.erb", plugin_path)
|
81
|
-
pkg.cp_erb("java/#{category}.java.erb", "src/main/java
|
82
|
-
pkg.cp_erb("java/test.java.erb", "src/test/java
|
91
|
+
pkg.cp_erb("java/#{category}.java.erb", "src/main/java/#{java_package_name.gsub(/\./, '/')}/#{java_class_name}.java")
|
92
|
+
pkg.cp_erb("java/test.java.erb", "src/test/java/#{java_package_name.gsub(/\./, '/')}/Test#{java_class_name}.java")
|
83
93
|
end
|
84
94
|
|
85
95
|
extra_guess_erb.each_pair do |erb,dest|
|
86
96
|
pkg.cp_erb(erb, dest)
|
87
97
|
end
|
88
98
|
|
99
|
+
puts ""
|
100
|
+
puts "Plugin template is successfully generated."
|
101
|
+
|
102
|
+
case language
|
103
|
+
when :ruby
|
104
|
+
puts "Next steps:"
|
105
|
+
puts ""
|
106
|
+
puts " $ cd #{full_project_name}"
|
107
|
+
puts " $ rake"
|
108
|
+
when :java
|
109
|
+
puts "Next steps:"
|
110
|
+
puts ""
|
111
|
+
puts " $ cd #{full_project_name}"
|
112
|
+
puts " $ ./gradlew package"
|
113
|
+
end
|
114
|
+
|
89
115
|
success = true
|
90
116
|
puts ""
|
91
117
|
ensure
|
92
|
-
FileUtils.rm_rf
|
118
|
+
FileUtils.rm_rf full_project_name unless success
|
93
119
|
end
|
94
120
|
end
|
95
121
|
end
|
@@ -290,9 +290,9 @@ examples:
|
|
290
290
|
puts ""
|
291
291
|
puts "Run following subcommands to try embulk:"
|
292
292
|
puts ""
|
293
|
-
puts " 1. guess #{File.join(path, 'example.yml')} -o config.yml"
|
294
|
-
puts " 2. preview config.yml"
|
295
|
-
puts " 3. run config.yml"
|
293
|
+
puts " 1. embulk guess #{File.join(path, 'example.yml')} -o config.yml"
|
294
|
+
puts " 2. embulk preview config.yml"
|
295
|
+
puts " 3. embulk run config.yml"
|
296
296
|
puts ""
|
297
297
|
|
298
298
|
when :new
|
@@ -35,8 +35,9 @@ TODO: Write short description here.
|
|
35
35
|
|
36
36
|
## Configuration
|
37
37
|
|
38
|
-
- **
|
39
|
-
- **
|
38
|
+
- **option1**: description (integer, required)
|
39
|
+
- **option2**: description (string, default: `"myvalue"`)
|
40
|
+
- **option3**: description (string, default: `null`)
|
40
41
|
|
41
42
|
## Example
|
42
43
|
|
@@ -45,46 +46,46 @@ TODO: Write short description here.
|
|
45
46
|
%when :input, :file_input
|
46
47
|
in:
|
47
48
|
type: <%= name %>
|
48
|
-
|
49
|
-
|
49
|
+
option1: example1
|
50
|
+
option2: example2
|
50
51
|
%when :output, :file_output
|
51
52
|
out:
|
52
53
|
type: <%= name %>
|
53
|
-
|
54
|
-
|
54
|
+
option1: example1
|
55
|
+
option2: example2
|
55
56
|
%when :filter
|
56
57
|
filters:
|
57
58
|
- type: <%= name %>
|
58
|
-
|
59
|
-
|
59
|
+
option1: example1
|
60
|
+
option2: example2
|
60
61
|
%when :parser
|
61
62
|
in:
|
62
63
|
type: any file input plugin type
|
63
64
|
parser:
|
64
65
|
type: <%= name %>
|
65
|
-
|
66
|
-
|
66
|
+
option1: example1
|
67
|
+
option2: example2
|
67
68
|
%when :formatter
|
68
69
|
out:
|
69
70
|
type: any output input plugin type
|
70
71
|
formatter:
|
71
72
|
type: <%= name %>
|
72
|
-
|
73
|
-
|
73
|
+
option1: example1
|
74
|
+
option2: example2
|
74
75
|
%when :decoder
|
75
76
|
in:
|
76
77
|
type: any output input plugin type
|
77
78
|
decoders:
|
78
79
|
- type: <%= name %>
|
79
|
-
|
80
|
-
|
80
|
+
option1: example1
|
81
|
+
option2: example2
|
81
82
|
%when :encoder
|
82
83
|
out:
|
83
84
|
type: any output input plugin type
|
84
85
|
encoders:
|
85
86
|
- type: <%= name %>
|
86
|
-
|
87
|
-
|
87
|
+
option1: example1
|
88
|
+
option2: example2
|
88
89
|
%end
|
89
90
|
```
|
90
91
|
|
@@ -93,7 +94,7 @@ out:
|
|
93
94
|
(If guess supported) you don't have to write `<%= category %>:` section in the configuration file. After writing `in:` section, you can let embulk guess `<%= category %>:` section using this command:
|
94
95
|
|
95
96
|
```
|
96
|
-
$ embulk gem install <%=
|
97
|
+
$ embulk gem install <%= full_project_name %>
|
97
98
|
$ embulk guess -g <%= name %> config.yml -o guessed.yml
|
98
99
|
```
|
99
100
|
%end
|