canoe 0.3.0.2 → 0.3.2.2

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.
data/lib/dependence.rb CHANGED
@@ -1,11 +1,10 @@
1
1
  require_relative 'source_files'
2
- require_relative 'err'
3
-
2
+ require_relative 'util'
4
3
  ##
5
4
  # class DepAnalyzer
6
- # This class is the key component of canoe, which offers file dependency analysis functionality.
5
+ # This class is the key component of canoe, which offers file dependency analysis functionality.
7
6
  # A DepAnalyzer takes a directory as input, sources files and corresponding header files in this
8
- # directory should have same name, i.e. test.cpp and test.hpp.
7
+ # directory should have same name, e.g. test.cpp and test.hpp.
9
8
  # DepAnalyzer would read every source file and recursively process user header files included in this source file to
10
9
  # find out all user header files this source file depends on.
11
10
  # Based on dependencies built in previous stage, DepAnalyzer determines which files should be recompiled and return
@@ -14,136 +13,140 @@ require_relative 'err'
14
13
  # Dependencies could be written to a file to avoid wasting time parsing all files, Depanalyzer would read from
15
14
  # this file to construct dependencies. But if sources files included new headers or included headers are revmoed,
16
15
  # Depanalyzer should rebuild the whole dependencies.
17
- class DepAnalyzer
18
- include Err
19
- def self.read_from(filename)
20
- File.open(filename, "r") do |f|
21
- ret = Hash.new []
22
- f.each_with_index do |line, i|
23
- entry = line.split(': ')
24
- Err.abort_on_err("Bad .canoe.deps format, line #{i+1}") unless entry.length == 2
25
- ret[entry[0]] = entry[1].split
26
- end
27
- ret
28
- end
29
- end
16
+ module Canoe
17
+ class DepAnalyzer
18
+ include Err
19
+ include SystemCommand
30
20
 
31
- def self.compiling_filter(deps, build_time, src_sfx='cpp', hdr_sfx='hpp')
32
- files = []
33
- @processed = {}
34
- @recompiles = {}
35
- deps.keys.each do |k|
36
- @processed[k] = false
37
- @recompiles[k] = false
38
- end
39
- deps.each do |k, v|
40
- next if k.end_with? ".#{hdr_sfx}"
41
- if should_recompile?(k, build_time)
42
- files << k
43
- @processed[k] = true
44
- @recompiles[k] = true
45
- next
21
+ class << self
22
+ include WorkSpaceUtil
23
+ def read_from(filename)
24
+ File.open(filename, 'r') do |f|
25
+ ret = Hash.new []
26
+ f.each_with_index do |line, i|
27
+ entry = line.split(': ')
28
+ abort_on_err("Bad .canoe.deps format, line #{i + 1}") unless entry.length == 2
29
+ ret[entry[0]] = entry[1].split
30
+ end
31
+ ret
32
+ end
46
33
  end
47
- v.each do |f|
48
- if mark(f, build_time, deps) || mark(f.sub(".#{hdr_sfx}", ".#{src_sfx}"), build_time, deps)
49
- files << k
50
- @processed[k] = true
51
- @recompiles[k] = true
52
- break
34
+
35
+ def compiling_filter(deps, build_time, src_sfx = 'cpp', hdr_sfx = 'hpp')
36
+ files = []
37
+ @processed = {}
38
+ @recompiles = {}
39
+ deps.each_key do |k|
40
+ @processed[k] = false
41
+ @recompiles[k] = false
42
+ end
43
+ deps.each do |k, v|
44
+ next if k.end_with? ".#{hdr_sfx}"
45
+
46
+ if should_recompile?(k, build_time)
47
+ files << k
48
+ @processed[k] = true
49
+ @recompiles[k] = true
50
+ next
51
+ end
52
+ v.each do |f|
53
+ next unless mark(f, build_time, deps) || mark(f.sub(".#{hdr_sfx}", ".#{src_sfx}"), build_time, deps)
54
+
55
+ files << k
56
+ @processed[k] = true
57
+ @recompiles[k] = true
58
+ break
59
+ end
53
60
  end
61
+ files
54
62
  end
55
- end
56
- files
57
- end
58
63
 
59
- private
60
- def self.mark(file, build_time, deps)
61
- ret = false
62
- return false unless File.exists? file
63
- if should_recompile?(file, build_time)
64
- return true
65
- else
66
- deps[file].each do |f|
67
- if @processed[f]
68
- ret |= @recompiles[f]
69
- next
64
+ private
65
+
66
+ def mark(file, build_time, deps)
67
+ ret = false
68
+ return false unless File.exist? file
69
+ return true if should_recompile?(file, build_time)
70
+
71
+ deps[file].each do |f|
72
+ if @processed[f]
73
+ ret |= @recompiles[f]
74
+ next
75
+ end
76
+ @processed[f] = true
77
+ if mark(f, build_time, deps)
78
+ @recompiles[f] = true
79
+ return true
80
+ end
70
81
  end
71
- @processed[f] = true
72
- if mark(f, build_time, deps)
73
- @recompiles[f] = true
74
- return true
82
+ ret
83
+ end
84
+
85
+ def should_recompile?(file, build_time)
86
+ judge = build_time
87
+ if build_time == Time.new(0)
88
+ objfile = file_to_obj(file)
89
+ return true unless File.exist? objfile
90
+
91
+ judge = File.mtime(objfile)
75
92
  end
93
+ File.mtime(file) > judge
76
94
  end
77
95
  end
78
- ret
79
- end
80
96
 
81
- def self.should_recompile?(file, build_time)
82
- judge = build_time
83
- if build_time == Time.new(0)
84
- objfile = if file.start_with?("./src/components")
85
- './obj/' + file.delete_suffix(File.extname(file))['./src/components/'.length..].gsub('/', '_') + '.o'
86
- else
87
- "./obj/#{File.basename(file, ".*")}.o"
88
- end
89
- return true unless File.exists? objfile
90
- judge = File.mtime(objfile)
97
+ def initialize(dir, src_sfx = 'cpp', hdr_sfx = 'hpp')
98
+ @dir = dir
99
+ @deps = Hash.new []
100
+ @source_suffix = src_sfx
101
+ @header_suffix = hdr_sfx
91
102
  end
92
- File.mtime(file) > judge
93
- end
94
103
 
95
- public
96
- def initialize(dir, src_sfx='cpp', hdr_sfx='hpp')
97
- @dir = dir
98
- @deps = Hash.new []
99
- @source_suffix = src_sfx
100
- @header_suffix = hdr_sfx
101
- end
104
+ def build_dependence(include_path)
105
+ files = SourceFiles.get_all(@dir) do |f|
106
+ f.end_with?(".#{@source_suffix}") || f.end_with?(".#{@header_suffix}")
107
+ end
102
108
 
103
- def build_dependence(include_path)
104
- files = SourceFiles.get_all(@dir) do |f|
105
- f.end_with?(".#{@source_suffix}") || f.end_with?(".#{@header_suffix}")
106
- end
109
+ @deps = Hash.new []
110
+ files.each do |fname|
111
+ @deps[fname] = get_all_headers include_path, fname, @header_suffix
112
+ end
107
113
 
108
- @deps = Hash.new []
109
- files.each do |fname|
110
- @deps[fname] = get_all_headers include_path, fname, @header_suffix
114
+ @deps
111
115
  end
112
116
 
113
- @deps
114
- end
117
+ def build_to_file(include_path, filename)
118
+ build_dependence include_path
115
119
 
116
- def build_to_file(include_path, filename)
117
- build_dependence include_path
118
-
119
- File.open(filename, "w") do |f|
120
- @deps.each do |k, v|
121
- f.write "#{k}: #{v.join(" ")}\n"
120
+ File.open(filename, 'w') do |f|
121
+ @deps.each do |k, v|
122
+ f.write "#{k}: #{v.join(' ')}\n"
123
+ end
122
124
  end
125
+
126
+ @deps
123
127
  end
124
-
125
- @deps
126
- end
127
128
 
128
- private
129
- def get_all_headers(include_path, file, suffix='hpp')
130
- File.open(file, "r") do |f|
131
- ret = []
132
- if file.end_with?(".#{@source_suffix}")
133
- header = file.sub(".#{@source_suffix}", ".#{@header_suffix}")
134
- ret += [header] if File.exists?(header)
135
- end
136
-
137
- f.each_line do |line|
138
- if mat = line.match(/include "(.+\.#{suffix})"/)
139
- include_path.each do |path|
140
- dep = "#{path}/#{mat[1]}"
141
- ret += [dep] if File.exists? dep
129
+ private
130
+
131
+ def get_all_headers(include_path, file, suffix = 'hpp')
132
+ File.open(file, 'r') do |f|
133
+ ret = []
134
+ if file.end_with?(".#{@source_suffix}")
135
+ header = file.sub(".#{@source_suffix}", ".#{@header_suffix}")
136
+ ret += [header] if File.exist?(header)
137
+ end
138
+
139
+ f.each_line do |line|
140
+ if (mat = line.match(/include "(.+\.#{suffix})"/))
141
+ include_path.each do |path|
142
+ dep = "#{path}/#{mat[1]}"
143
+ ret += [dep] if File.exist? dep
144
+ end
142
145
  end
143
146
  end
147
+
148
+ ret.uniq
144
149
  end
145
-
146
- ret.uniq
147
150
  end
148
151
  end
149
152
  end
data/lib/source_files.rb CHANGED
@@ -5,27 +5,28 @@ class SourceFiles
5
5
  class << self
6
6
  def get_all(dir, &block)
7
7
  @files = []
8
- get_all_helper(dir, &block)
8
+ get_all_helper(dir, &block)
9
9
  @files
10
10
  end
11
11
 
12
- def get_in(dir, &block)
12
+ def get_in(dir)
13
13
  @files = []
14
14
  Dir.each_child(dir) do |f|
15
15
  file = "#{dir}/#{f}"
16
16
  if File.file? file
17
17
  if block_given?
18
- @files << "#{file}" if yield(f)
18
+ @files << file.to_s if yield(f)
19
19
  else
20
- @files << "#{file}"
20
+ @files << file.to_s
21
21
  end
22
- end
22
+ end
23
23
  end
24
-
24
+
25
25
  @files
26
26
  end
27
27
 
28
28
  private
29
+
29
30
  def get_all_helper(dir, &block)
30
31
  Dir.each_child(dir) do |f|
31
32
  file = "#{dir}/#{f}"
@@ -42,4 +43,3 @@ class SourceFiles
42
43
  end
43
44
  end
44
45
  end
45
-
data/lib/util.rb ADDED
@@ -0,0 +1,84 @@
1
+ require_relative 'coloring'
2
+
3
+ module Canoe
4
+ ##
5
+ # Stepper record the progress of a task
6
+ # progress is obtained via #'progress_as_str
7
+ class Stepper
8
+ def initialize(total, togo)
9
+ @total = total.to_f
10
+ @togo = togo.to_f
11
+ end
12
+
13
+ def progress_as_str
14
+ progress = ((@total - @togo) / @total).round(2) * 100
15
+ "[#{progress.to_i}%%]"
16
+ end
17
+
18
+ def step
19
+ @togo -= 1 if @togo.positive?
20
+ end
21
+ end
22
+
23
+ ##
24
+ # wrapping workspace related functionality to expose to other modules
25
+ module WorkSpaceUtil
26
+ def current_workspace
27
+ abort_on_err 'not in a canoe workspace' unless File.exist? '.canoe'
28
+ config = ConfigReader.extract_flags('config.json')
29
+
30
+ src_sfx = config['source-suffix'] || 'cpp'
31
+ hdr_sfx = config['header-suffix'] || 'hpp'
32
+
33
+ name = Dir.pwd.split('/')[-1]
34
+ mode = File.exist?("src/main.#{src_sfx}") ? :bin : :lib
35
+
36
+ WorkSpace.new(name, mode, src_sfx, hdr_sfx)
37
+ end
38
+
39
+ def src_to_obj(src)
40
+ current_workspace.src_to_obj(src)
41
+ end
42
+
43
+ def comp_to_obj(comp)
44
+ current_workspace.comp_to_obj(comp)
45
+ end
46
+
47
+ def file_to_obj(file)
48
+ current_workspace.file_to_obj(file)
49
+ end
50
+
51
+ def extract_one_file(file, deps)
52
+ current_workspace.extract_one_file(file, deps)
53
+ end
54
+
55
+ def extract_one_file_obj(file, deps)
56
+ current_workspace.extract_one_file_obj(file, deps)
57
+ end
58
+ end
59
+
60
+ module SystemCommand
61
+ def issue_command(cmd_str)
62
+ puts cmd_str
63
+ system cmd_str
64
+ end
65
+ end
66
+
67
+ module Err
68
+ def warn_on_err(err)
69
+ puts <<~ERR
70
+ #{'Waring: '.yellow}
71
+ #{err}
72
+ try 'canoe help' for more information
73
+ ERR
74
+ end
75
+
76
+ def abort_on_err(err)
77
+ abort <<~ERR
78
+ #{'Fatal: '.red}
79
+ #{err}
80
+ try 'canoe help' for more information
81
+ ERR
82
+ end
83
+ end
84
+ end
data/lib/workspace/add.rb CHANGED
@@ -1,26 +1,29 @@
1
- class WorkSpace
2
- def add(args)
3
- args.each do |i|
4
- dir = @components
5
- filenames = i.split("/")
6
- prefix = []
7
- filenames.each do |filename|
8
- dir += "/#{filename}"
9
- prefix << filename
10
- unless Dir.exist? dir
11
- FileUtils.mkdir dir
1
+ module Canoe
2
+ class WorkSpace
3
+ def add(args)
4
+ args.each do |i|
5
+ dir = @components
6
+ filenames = i.split '/'
7
+ prefix = []
8
+ filenames.each do |filename|
9
+ dir += "/#{filename}"
10
+ prefix << filename
11
+ next if Dir.exist? dir
12
+
13
+ FileUtils.mkdir dir
12
14
  Dir.chdir(dir) do
13
- puts "created " + Dir.pwd.blue
15
+ puts "created + #{Dir.pwd.blue}"
14
16
  create_working_files prefix.join('__'), filename
15
17
  end
16
18
  end
17
19
  end
18
20
  end
19
- end
20
21
 
21
- private
22
- def create_working_files(prefix, filename)
23
- DefaultFiles.create_cpp filename, @source_suffix, @header_suffix
24
- DefaultFiles.create_hpp @name, prefix, filename, @header_suffix
22
+ private
23
+
24
+ def create_working_files(prefix, filename)
25
+ DefaultFiles.create_cpp filename, @source_suffix, @header_suffix
26
+ DefaultFiles.create_hpp @name, prefix, filename, @header_suffix
27
+ end
25
28
  end
26
- end
29
+ end
@@ -1,130 +1,190 @@
1
- class WorkSpace
2
- # args are commandline parameters passed to `canoe build`,
3
- # could be 'all', 'test', 'target' or empty
4
- def build(args)
5
- case args[0]
6
- when "all"
7
- build_all
8
- when "test"
9
- build_test
10
- else
11
- build_target
1
+ require 'json'
2
+ module Canoe
3
+ class WorkSpace
4
+ def src_to_obj(src)
5
+ @obj_prefix + File.basename(src, ".*") + ".o"
12
6
  end
13
- end
14
7
 
15
- private
16
- def build_flags(flags, config)
17
- config.values.each do |v|
18
- case v
19
- when String
20
- flags << v
21
- when Array
22
- v.each do |o|
23
- flags << o
24
- end
8
+ def comp_to_obj(comp)
9
+ @obj_prefix + comp.delete_suffix(File.extname(comp))[@components_prefix.length..].gsub("/", "_") + ".o"
10
+ end
11
+
12
+ # the if else order is important because tests are regarded as sources
13
+ def file_to_obj(file)
14
+ if file.start_with?(@components_prefix)
15
+ comp_to_obj file
25
16
  else
26
- abort_on_err "unknown options in config.json, #{v}"
17
+ src_to_obj file
27
18
  end
28
19
  end
29
- end
30
20
 
31
- def build_compiler_from_config
32
- Dir.chdir(@workspace) do
21
+ # args are commandline parameters passed to `canoe build`,
22
+ # could be 'all', 'test', 'target', 'base' or empty
23
+ def build(arg = 'target')
24
+ send "build_#{arg}"
25
+ end
26
+
27
+ private
28
+
29
+ def build_flags(flags, config)
30
+ config.values.each do |v|
31
+ case v
32
+ when String
33
+ flags << v
34
+ when Array
35
+ v.each do |o|
36
+ flags << o
37
+ end
38
+ else
39
+ abort_on_err "unknown options in config.json, #{v}"
40
+ end
41
+ end
42
+ end
43
+
44
+ def build_compiler_from_config
33
45
  flags = ConfigReader.extract_flags "config.json"
34
- compiler_name = flags['compiler'] ? flags['compiler'] : "clang++"
35
- abort_on_err "compiler #{compiler_name} not found" unless File.exists?("/usr/bin/#{compiler_name}")
36
- compiler_flags = ['-Isrc/components']
46
+ compiler_name = flags["compiler"] ? flags["compiler"] : "clang++"
47
+
48
+ abort_on_err "compiler #{compiler_name} not found" unless system "which #{compiler_name} > /dev/null"
49
+ compiler_flags = ["-Isrc/components"]
37
50
  linker_flags = []
38
51
 
39
- c_flags, l_flags = flags['flags']['compile'], flags['flags']['link']
52
+ c_flags, l_flags = flags["flags"]["compile"], flags["flags"]["link"]
40
53
  build_flags(compiler_flags, c_flags)
41
54
  build_flags(linker_flags, l_flags)
42
55
 
43
56
  @compiler = Compiler.new compiler_name, compiler_flags, linker_flags
44
57
  end
45
- end
46
58
 
47
- def compile(f, o)
48
- @compiler.compile f, o
49
- end
59
+ def compile(f, o)
60
+ @compiler.compile f, o
61
+ end
50
62
 
51
- def link_exectutable(odir, objs)
52
- puts "#{"[100%]".green} linking"
53
- @compiler.link_executable "#{odir}/#{@name}", objs
54
- end
63
+ def link_exectutable(odir, objs)
64
+ puts "#{"[100%]".green} linking"
65
+ @compiler.link_executable "#{odir}/#{@name}", objs
66
+ end
55
67
 
56
- def link_shared(odir, objs)
57
- puts "#{"[100%]".green} linking"
58
- @compiler.link_shared "#{odir}/lib#{@name}", objs
59
- end
68
+ def link_shared(odir, objs)
69
+ puts "#{"[100%]".green} linking"
70
+ @compiler.link_shared "#{odir}/lib#{@name}", objs
71
+ end
60
72
 
61
- def build_bin(files)
62
- # return if files.empty?
63
- build_compiler_from_config
64
- if build_common(files) && link_exectutable('./target', Dir.glob("obj/*.o"))
73
+ def build_bin(files)
74
+ if build_common(files) &&
75
+ link_exectutable(@target_short, Dir.glob("obj/*.o").reject { |f| f.start_with? 'obj/test_' })
65
76
  puts "BUILDING SUCCEEDED".green
66
- else
67
- puts "building FAILED".red
77
+ return true
78
+ else
79
+ puts "building target FAILED".red
80
+ return false
81
+ end
68
82
  end
69
- end
70
83
 
71
- def build_lib(files)
72
- # return if files.empty?
73
- build_compiler_from_config
74
- @compiler.append_compiling_flag '-fPIC'
75
- if build_common(files) && link_shared('./target', Dir.glob("obj/*.o"))
76
- puts "BUILDING SUCCEEDED".green
77
- else
78
- puts "building FAILED".red
84
+ def build_lib(files)
85
+ @compiler.append_compiling_flag "-fPIC"
86
+ if build_common(files) &&
87
+ link_shared(@target_short, Dir.glob("obj/*.o").reject { |f| f.start_with? 'obj/test_'})
88
+ puts "BUILDING SUCCEEDED".green
89
+ else
90
+ puts "building target FAILED".red
91
+ end
79
92
  end
80
- end
81
93
 
82
- def build_common(files)
83
- all = SourceFiles.get_all('./src') {|f| f.end_with? @source_suffix}
84
- total = all.size.to_f
85
- compiled = total - files.size
86
- comps = files.select {|f| f.start_with? @components_prefix}
87
- srcs = files - comps
88
- flag = true;
89
-
90
- srcs.each do |f|
91
- progress = (compiled / total).round(2) * 100
92
- printf "[#{progress.to_i}%%]".green + " compiling #{f}: "
93
- fname = f.split("/")[-1]
94
- o = @obj_prefix + File.basename(fname, ".*") + '.o'
95
- flag = false unless compile f, o
96
- compiled += 1
97
- end
98
-
99
- comps.each do |f|
100
- progress = (compiled / total).round(2) * 100
101
- printf "[#{progress.to_i}%%]".green + " compiling #{f}: "
102
- o = @obj_prefix + f.delete_suffix(File.extname(f))[@components_prefix.length..]
103
- .gsub('/', '_') + '.o'
104
- flag = false unless compile f, o
105
- compiled += 1
106
- end
107
- flag
108
- end
94
+ def build_common(files)
95
+ all = SourceFiles.get_all(@src_short) { |f| f.end_with? @source_suffix }
96
+ stepper = Stepper.new all.size, files.size
97
+ flag = true
109
98
 
110
- def build_all
111
- build_target
112
- build_test
99
+ files.each do |f|
100
+ progress = stepper.progress_as_str.green
101
+ printf "#{progress.green} compiling #{f.yellow}: "
102
+ o = file_to_obj(f)
103
+ flag = false unless compile f, o
104
+ stepper.step
105
+ end
106
+ flag
107
+ end
108
+
109
+ def build_all
110
+ build_target
111
+ build_test
112
+ end
113
+
114
+ def get_deps(dep_file, source_dir, include_dirs)
115
+ File.exist?(dep_file) ? DepAnalyzer.read_from(dep_file) :
116
+ DepAnalyzer.new(source_dir, @source_suffix, @header_suffix).build_to_file(include_dirs, dep_file)
117
+ end
118
+
119
+ def target_deps
120
+ get_deps @deps, @src_short, [@src_short, @components_short]
121
+ end
122
+
123
+ # contain only headers
124
+ # sources in ./src/components are not included
125
+ def tests_deps
126
+ get_deps @test_deps, @tests_short, [@src_short, @components_short]
127
+ end
128
+
129
+ def build_target
130
+ puts "#{'[BUILDING TARGET]'.magenta}..."
131
+ target = "#{@target}/#{@name}"
132
+ build_time = File.exist?(target) ? File.mtime(target) : Time.new(0)
133
+ files = DepAnalyzer.compiling_filter target_deps, build_time, @source_suffix, @header_suffix
134
+
135
+ build_compiler_from_config
136
+
137
+ if files.empty? && File.exist?(target)
138
+ puts "nothing to do, all up to date"
139
+ return true
140
+ end
141
+
142
+ self.send "build_#{@mode.to_s}", files
143
+ end
144
+
145
+ # generate a compile_commands.json file
146
+ def build_base
147
+ deps = target_deps.merge tests_deps
148
+ build_compiler_from_config
149
+ database = CompilationDatabase.new
150
+ deps.each_key do |k|
151
+ next if k.end_with? @header_suffix
152
+ c = @compiler.name.end_with?('++') ? 'c++' : 'c'
153
+
154
+ arg = [c] + @compiler.compiling_flags_as_str.split + [k] + [file_to_obj(k)] + ['-c', '-o']
155
+ database.add_command_object(@workspace, arg, k)
156
+ end
157
+ File.open('compile_commands.json', 'w') do |f|
158
+ f.puts database.pretty_to_s
159
+ end
160
+ end
113
161
  end
114
162
 
115
- def build_target
116
- deps = File.exist?(@deps) ?
117
- DepAnalyzer.read_from(@deps) :
118
- DepAnalyzer.new('./src').build_to_file(['./src', './src/components'], @deps)
119
- target = "./target/#{@name}"
120
- build_time = File.exist?(target) ? File.mtime(target) : Time.new(0)
121
- files = DepAnalyzer.compiling_filter(deps, build_time, @source_suffix, @header_suffix)
163
+ class CompilationDatabase
164
+ attr_reader :database
165
+ def initialize
166
+ @database = []
167
+ end
168
+
169
+ def add_command_object(dir, arguments, file)
170
+ temp = {
171
+ "arguments" => arguments,
172
+ "directory" => dir,
173
+ "file" => file
174
+ }
175
+ @database << temp
176
+ end
122
177
 
123
- if files.empty? && File.exist?(target)
124
- puts "nothing to do, all up to date"
125
- return
178
+ def to_s
179
+ @database.to_s
126
180
  end
127
181
 
128
- self.send "build_#{@mode.to_s}", files
182
+ def pretty_to_s
183
+ JSON.pretty_generate(@database)
184
+ end
185
+
186
+ def to_json
187
+ @database.to_json
188
+ end
129
189
  end
130
- end
190
+ end