cure 0.1.1 → 0.4.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +16 -3
- data/.tool-versions +1 -0
- data/Dockerfile +1 -1
- data/Gemfile +1 -0
- data/Gemfile.lock +25 -6
- data/README.md +59 -81
- data/docs/README.md +33 -0
- data/docs/about.md +219 -0
- data/docs/builder/add.md +52 -0
- data/docs/builder/black_white_list.md +83 -0
- data/docs/builder/copy.md +48 -0
- data/docs/builder/explode.md +70 -0
- data/docs/builder/main.md +43 -0
- data/docs/builder/remove.md +46 -0
- data/docs/examples/examples.md +164 -0
- data/docs/export/main.md +37 -0
- data/docs/extract/main.md +89 -0
- data/docs/metadata/main.md +29 -0
- data/docs/query/main.md +45 -0
- data/docs/sources/main.md +36 -0
- data/docs/transform/main.md +53 -0
- data/docs/validate/main.md +42 -0
- data/exe/cure +12 -37
- data/exe/cure.old +59 -0
- data/lib/cure/builder/base_builder.rb +151 -0
- data/lib/cure/builder/candidate.rb +56 -0
- data/lib/cure/cli/command.rb +105 -0
- data/lib/cure/cli/generate_command.rb +54 -0
- data/lib/cure/cli/new_command.rb +52 -0
- data/lib/cure/cli/run_command.rb +19 -0
- data/lib/cure/cli/templates/README.md.erb +1 -0
- data/lib/cure/cli/templates/gemfile.erb +5 -0
- data/lib/cure/cli/templates/gitignore.erb +181 -0
- data/lib/cure/cli/templates/new_template.rb.erb +31 -0
- data/lib/cure/cli/templates/tool-versions.erb +1 -0
- data/lib/cure/config.rb +151 -13
- data/lib/cure/coordinator.rb +108 -0
- data/lib/cure/database.rb +191 -0
- data/lib/cure/dsl/builder.rb +26 -0
- data/lib/cure/dsl/exporters.rb +45 -0
- data/lib/cure/dsl/extraction.rb +60 -0
- data/lib/cure/dsl/metadata.rb +33 -0
- data/lib/cure/dsl/queries.rb +36 -0
- data/lib/cure/dsl/source_files.rb +36 -0
- data/lib/cure/dsl/template.rb +131 -0
- data/lib/cure/dsl/transformations.rb +95 -0
- data/lib/cure/dsl/validator.rb +22 -0
- data/lib/cure/export/base_processor.rb +194 -0
- data/lib/cure/export/manager.rb +24 -0
- data/lib/cure/extract/base_processor.rb +47 -0
- data/lib/cure/extract/csv_lookup.rb +43 -0
- data/lib/cure/extract/extractor.rb +80 -0
- data/lib/cure/extract/filter.rb +118 -0
- data/lib/cure/extract/named_range.rb +94 -0
- data/lib/cure/extract/named_range_processor.rb +128 -0
- data/lib/cure/extract/variable.rb +25 -0
- data/lib/cure/extract/variable_processor.rb +57 -0
- data/lib/cure/generator/base_generator.rb +61 -0
- data/lib/cure/generator/case_generator.rb +32 -0
- data/lib/cure/generator/character_generator.rb +41 -0
- data/lib/cure/generator/erb_generator.rb +21 -0
- data/lib/cure/generator/eval_generator.rb +34 -0
- data/lib/cure/generator/faker_generator.rb +31 -0
- data/lib/cure/generator/guid_generator.rb +21 -0
- data/lib/cure/generator/hex_generator.rb +21 -0
- data/lib/cure/generator/imports.rb +16 -0
- data/lib/cure/generator/number_generator.rb +21 -0
- data/lib/cure/generator/placeholder_generator.rb +26 -0
- data/lib/cure/generator/proc_generator.rb +21 -0
- data/lib/cure/generator/redact_generator.rb +22 -0
- data/lib/cure/generator/static_generator.rb +21 -0
- data/lib/cure/generator/variable_generator.rb +26 -0
- data/lib/cure/helpers/file_helpers.rb +50 -0
- data/lib/cure/helpers/object_helpers.rb +17 -0
- data/lib/cure/helpers/perf_helpers.rb +30 -0
- data/lib/cure/helpers/string.rb +54 -0
- data/lib/cure/launcher.rb +125 -0
- data/lib/cure/log.rb +10 -3
- data/lib/cure/planner.rb +136 -0
- data/lib/cure/strategy/append_strategy.rb +28 -0
- data/lib/cure/strategy/base_strategy.rb +98 -0
- data/lib/cure/strategy/contain_strategy.rb +51 -0
- data/lib/cure/strategy/end_with_strategy.rb +52 -0
- data/lib/cure/strategy/full_strategy.rb +28 -0
- data/lib/cure/strategy/history/history_cache.rb +82 -0
- data/lib/cure/strategy/imports.rb +12 -0
- data/lib/cure/strategy/match_strategy.rb +48 -0
- data/lib/cure/strategy/prepend_strategy.rb +28 -0
- data/lib/cure/strategy/regex_strategy.rb +55 -0
- data/lib/cure/strategy/split_strategy.rb +58 -0
- data/lib/cure/strategy/start_with_strategy.rb +53 -0
- data/lib/cure/transformation/candidate.rb +47 -36
- data/lib/cure/transformation/transform.rb +29 -71
- data/lib/cure/validator/base_rule.rb +78 -0
- data/lib/cure/validator/candidate.rb +54 -0
- data/lib/cure/validator/manager.rb +21 -0
- data/lib/cure/validators.rb +71 -0
- data/lib/cure/version.rb +1 -1
- data/lib/cure.rb +19 -6
- data/templates/dsl_example.rb +48 -0
- data/templates/empty_template.rb +31 -0
- metadata +161 -23
- data/lib/cure/csv_helpers.rb +0 -6
- data/lib/cure/export/exporter.rb +0 -49
- data/lib/cure/file_helpers.rb +0 -38
- data/lib/cure/generator/base.rb +0 -148
- data/lib/cure/main.rb +0 -63
- data/lib/cure/object_helpers.rb +0 -27
- data/lib/cure/strategy/base.rb +0 -223
- data/templates/aws_cur_template.json +0 -143
- data/templates/example_template.json +0 -38
@@ -0,0 +1,181 @@
|
|
1
|
+
# Created by https://www.toptal.com/developers/gitignore/api/rubymine,ruby
|
2
|
+
# Edit at https://www.toptal.com/developers/gitignore?templates=rubymine,ruby
|
3
|
+
|
4
|
+
.idea
|
5
|
+
|
6
|
+
/sandpit
|
7
|
+
|
8
|
+
/.bundle/
|
9
|
+
/.yardoc
|
10
|
+
/_yardoc/
|
11
|
+
/coverage/
|
12
|
+
/doc/
|
13
|
+
/pkg/
|
14
|
+
/spec/reports/
|
15
|
+
/tmp/
|
16
|
+
|
17
|
+
# rspec failure tracking
|
18
|
+
.rspec_status
|
19
|
+
|
20
|
+
### Ruby ###
|
21
|
+
*.gem
|
22
|
+
*.rbc
|
23
|
+
/.config
|
24
|
+
/InstalledFiles
|
25
|
+
/spec/examples.txt
|
26
|
+
/test/tmp/
|
27
|
+
/test/version_tmp/
|
28
|
+
|
29
|
+
# Used by dotenv library to load environment variables.
|
30
|
+
# .env
|
31
|
+
|
32
|
+
# Ignore Byebug command history file.
|
33
|
+
.byebug_history
|
34
|
+
|
35
|
+
## Specific to RubyMotion:
|
36
|
+
.dat*
|
37
|
+
.repl_history
|
38
|
+
build/
|
39
|
+
*.bridgesupport
|
40
|
+
build-iPhoneOS/
|
41
|
+
build-iPhoneSimulator/
|
42
|
+
|
43
|
+
## Specific to RubyMotion (use of CocoaPods):
|
44
|
+
#
|
45
|
+
# We recommend against adding the Pods directory to your .gitignore. However
|
46
|
+
# you should judge for yourself, the pros and cons are mentioned at:
|
47
|
+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
48
|
+
# vendor/Pods/
|
49
|
+
|
50
|
+
## Documentation cache and generated files:
|
51
|
+
/.yardoc/
|
52
|
+
/rdoc/
|
53
|
+
|
54
|
+
## Environment normalization:
|
55
|
+
/vendor/bundle
|
56
|
+
/lib/bundler/man/
|
57
|
+
|
58
|
+
# for a library or gem, you might want to ignore these files since the code is
|
59
|
+
# intended to run in multiple environments; otherwise, check them in:
|
60
|
+
# Gemfile.lock
|
61
|
+
# .ruby-version
|
62
|
+
# .ruby-gemset
|
63
|
+
|
64
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
65
|
+
.rvmrc
|
66
|
+
|
67
|
+
# Used by RuboCop. Remote config files pulled in from inherit_from directive.
|
68
|
+
# .rubocop-https?--*
|
69
|
+
|
70
|
+
### RubyMine ###
|
71
|
+
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
72
|
+
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
73
|
+
|
74
|
+
# User-specific stuff
|
75
|
+
.idea/**/workspace.xml
|
76
|
+
.idea/**/tasks.xml
|
77
|
+
.idea/**/usage.statistics.xml
|
78
|
+
.idea/**/dictionaries
|
79
|
+
.idea/**/shelf
|
80
|
+
|
81
|
+
# AWS User-specific
|
82
|
+
.idea/**/aws.xml
|
83
|
+
|
84
|
+
# Generated files
|
85
|
+
.idea/**/contentModel.xml
|
86
|
+
|
87
|
+
# Sensitive or high-churn files
|
88
|
+
.idea/**/dataSources/
|
89
|
+
.idea/**/dataSources.ids
|
90
|
+
.idea/**/dataSources.local.xml
|
91
|
+
.idea/**/sqlDataSources.xml
|
92
|
+
.idea/**/dynamic.xml
|
93
|
+
.idea/**/uiDesigner.xml
|
94
|
+
.idea/**/dbnavigator.xml
|
95
|
+
|
96
|
+
# Gradle
|
97
|
+
.idea/**/gradle.xml
|
98
|
+
.idea/**/libraries
|
99
|
+
|
100
|
+
# Gradle and Maven with auto-import
|
101
|
+
# When using Gradle or Maven with auto-import, you should exclude module files,
|
102
|
+
# since they will be recreated, and may cause churn. Uncomment if using
|
103
|
+
# auto-import.
|
104
|
+
# .idea/artifacts
|
105
|
+
# .idea/compiler.xml
|
106
|
+
# .idea/jarRepositories.xml
|
107
|
+
# .idea/modules.xml
|
108
|
+
# .idea/*.iml
|
109
|
+
# .idea/modules
|
110
|
+
# *.iml
|
111
|
+
# *.ipr
|
112
|
+
|
113
|
+
# CMake
|
114
|
+
cmake-build-*/
|
115
|
+
|
116
|
+
# Mongo Explorer plugin
|
117
|
+
.idea/**/mongoSettings.xml
|
118
|
+
|
119
|
+
# File-based project format
|
120
|
+
*.iws
|
121
|
+
|
122
|
+
# IntelliJ
|
123
|
+
out/
|
124
|
+
|
125
|
+
# mpeltonen/sbt-idea plugin
|
126
|
+
.idea_modules/
|
127
|
+
|
128
|
+
# JIRA plugin
|
129
|
+
atlassian-ide-plugin.xml
|
130
|
+
|
131
|
+
# Cursive Clojure plugin
|
132
|
+
.idea/replstate.xml
|
133
|
+
|
134
|
+
# Crashlytics plugin (for Android Studio and IntelliJ)
|
135
|
+
com_crashlytics_export_strings.xml
|
136
|
+
crashlytics.properties
|
137
|
+
crashlytics-build.properties
|
138
|
+
fabric.properties
|
139
|
+
|
140
|
+
# Editor-based Rest Client
|
141
|
+
.idea/httpRequests
|
142
|
+
|
143
|
+
# Android studio 3.1+ serialized cache file
|
144
|
+
.idea/caches/build_file_checksums.ser
|
145
|
+
|
146
|
+
### RubyMine Patch ###
|
147
|
+
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
|
148
|
+
|
149
|
+
# *.iml
|
150
|
+
# modules.xml
|
151
|
+
# .idea/misc.xml
|
152
|
+
# *.ipr
|
153
|
+
|
154
|
+
# Sonarlint plugin
|
155
|
+
# https://plugins.jetbrains.com/plugin/7973-sonarlint
|
156
|
+
.idea/**/sonarlint/
|
157
|
+
|
158
|
+
# SonarQube Plugin
|
159
|
+
# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
|
160
|
+
.idea/**/sonarIssues.xml
|
161
|
+
|
162
|
+
# Markdown Navigator plugin
|
163
|
+
# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
|
164
|
+
.idea/**/markdown-navigator.xml
|
165
|
+
.idea/**/markdown-navigator-enh.xml
|
166
|
+
.idea/**/markdown-navigator/
|
167
|
+
|
168
|
+
# Cache file creation bug
|
169
|
+
# See https://youtrack.jetbrains.com/issue/JBR-2257
|
170
|
+
.idea/$CACHE_FILE$
|
171
|
+
|
172
|
+
# CodeStream plugin
|
173
|
+
# https://plugins.jetbrains.com/plugin/12206-codestream
|
174
|
+
.idea/codestream.xml
|
175
|
+
|
176
|
+
# End of https://www.toptal.com/developers/gitignore/api/rubymine,ruby
|
177
|
+
|
178
|
+
/input/*
|
179
|
+
!/input/.gitkeep
|
180
|
+
/output/*
|
181
|
+
!/output/.gitkeep
|
@@ -0,0 +1,31 @@
|
|
1
|
+
## Empty template, uncomment the sections that you need!
|
2
|
+
#
|
3
|
+
# metadata do
|
4
|
+
# name ""
|
5
|
+
# version ""
|
6
|
+
# comments ""
|
7
|
+
# additional data: {
|
8
|
+
# # Any additional data can go here. For example:
|
9
|
+
# key: "value"
|
10
|
+
# }
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# extract do
|
14
|
+
# # ...
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# build do
|
18
|
+
# # ...
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# query do
|
22
|
+
# # ...
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# transform do
|
26
|
+
# # ...
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# export do
|
30
|
+
# # ...
|
31
|
+
# end
|
@@ -0,0 +1 @@
|
|
1
|
+
ruby 3.2.1
|
data/lib/cure/config.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require "cure"
|
4
4
|
require "json"
|
5
5
|
require "singleton"
|
6
|
+
require "tempfile"
|
6
7
|
|
7
8
|
module Cure
|
8
9
|
module Configuration
|
@@ -14,30 +15,46 @@ module Cure
|
|
14
15
|
conf
|
15
16
|
end
|
16
17
|
|
18
|
+
# @param [Array<Cure::Configuration::CsvFileProxy>] source_files
|
19
|
+
# @param [Cure::Template] template
|
20
|
+
# @return [Config]
|
21
|
+
def create_config(source_files, template)
|
22
|
+
Config.new(source_files, template)
|
23
|
+
end
|
24
|
+
|
17
25
|
# @param [Config] request_config
|
18
26
|
def register_config(request_config)
|
19
27
|
ConfigurationSource.instance.load_config(request_config)
|
20
28
|
end
|
21
29
|
|
22
|
-
#
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
30
|
+
# If we are overloading here as a "data store" and "config store", we
|
31
|
+
# could break out variables and placeholders into their own singleton.
|
32
|
+
#
|
33
|
+
# This should be a kind of instance cache, which loads once per run,
|
34
|
+
# and junk can be jammed in there?
|
27
35
|
class Config
|
28
|
-
attr_accessor :source_file_location, :template, :output_dir
|
29
36
|
|
30
|
-
# @
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
37
|
+
# @return Array<Cure::Configuration::CsvFileProxy>
|
38
|
+
attr_accessor :source_files
|
39
|
+
|
40
|
+
# @return [Cure::Template]
|
41
|
+
attr_accessor :template
|
42
|
+
|
43
|
+
# @param [Array<Cure::Configuration::CsvFileProxy>] source_files
|
44
|
+
# @param [Cure::Template] template
|
45
|
+
def initialize(source_files, template)
|
46
|
+
@source_files = source_files
|
35
47
|
@template = template
|
36
|
-
@output_dir = output_dir
|
37
48
|
end
|
38
49
|
|
39
50
|
def placeholders
|
40
|
-
@template
|
51
|
+
@template.transformations.placeholders || {}
|
52
|
+
end
|
53
|
+
|
54
|
+
def with_source_file(&block)
|
55
|
+
@source_files.each_with_index do |file, _idx|
|
56
|
+
file.with_file(&block)
|
57
|
+
end
|
41
58
|
end
|
42
59
|
end
|
43
60
|
|
@@ -52,5 +69,126 @@ module Cure
|
|
52
69
|
@config = config
|
53
70
|
end
|
54
71
|
end
|
72
|
+
|
73
|
+
class CsvFileProxy
|
74
|
+
|
75
|
+
# @return [TrueClass,FalseClass]
|
76
|
+
attr_reader :valid
|
77
|
+
# @return [DefaultFileHandler]
|
78
|
+
attr_reader :csv_handler
|
79
|
+
|
80
|
+
# @return [CsvFileProxy]
|
81
|
+
def self.load_file(type, obj, ref_name)
|
82
|
+
handler =
|
83
|
+
case type
|
84
|
+
when :file
|
85
|
+
FileHandler.new(obj, ref_name)
|
86
|
+
when :file_contents
|
87
|
+
FileContentsHandler.new(obj, ref_name)
|
88
|
+
when :path, :pathname
|
89
|
+
PathnameHandler.new(obj, ref_name)
|
90
|
+
else
|
91
|
+
raise "Invalid file type handler [#{type}]. Supported: [:file, :file_contents, :path, :pathname]"
|
92
|
+
end
|
93
|
+
|
94
|
+
new(handler)
|
95
|
+
end
|
96
|
+
|
97
|
+
# @return [DefaultFileHandler]
|
98
|
+
def initialize(csv_handler)
|
99
|
+
@csv_handler = csv_handler
|
100
|
+
end
|
101
|
+
|
102
|
+
def description
|
103
|
+
@csv_handler.description
|
104
|
+
end
|
105
|
+
|
106
|
+
def with_file(&block)
|
107
|
+
@csv_handler.with_file(&block)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class DefaultFileHandler
|
112
|
+
attr_reader :type, :ref_name
|
113
|
+
|
114
|
+
def initialize(type, ref_name)
|
115
|
+
@type = type
|
116
|
+
@ref_name = ref_name
|
117
|
+
end
|
118
|
+
|
119
|
+
def with_file(&_block)
|
120
|
+
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
121
|
+
end
|
122
|
+
|
123
|
+
def description
|
124
|
+
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class PathnameHandler < DefaultFileHandler
|
129
|
+
attr_accessor :pathname
|
130
|
+
|
131
|
+
# @param [Pathname] pathname
|
132
|
+
def initialize(pathname, ref_name)
|
133
|
+
super(:pathname, ref_name)
|
134
|
+
@pathname = pathname
|
135
|
+
end
|
136
|
+
|
137
|
+
def with_file(&_block)
|
138
|
+
yield @pathname, @ref_name
|
139
|
+
end
|
140
|
+
|
141
|
+
def description
|
142
|
+
@pathname.to_s
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
class FileHandler < DefaultFileHandler
|
147
|
+
# @return [File]
|
148
|
+
attr_accessor :file
|
149
|
+
|
150
|
+
# @param [File] file
|
151
|
+
def initialize(file, ref_name)
|
152
|
+
super(:file, ref_name)
|
153
|
+
@file = file
|
154
|
+
end
|
155
|
+
|
156
|
+
def with_file(&_block)
|
157
|
+
yield @file, @ref_name
|
158
|
+
|
159
|
+
ensure
|
160
|
+
@file&.close
|
161
|
+
end
|
162
|
+
|
163
|
+
def description
|
164
|
+
File.basename(file)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
class FileContentsHandler < DefaultFileHandler
|
169
|
+
# @return [String]
|
170
|
+
attr_accessor :file_contents
|
171
|
+
|
172
|
+
# @param [String] file_contents
|
173
|
+
def initialize(file_contents, ref_name)
|
174
|
+
super(:file_contents, ref_name)
|
175
|
+
@file_contents = file_contents
|
176
|
+
end
|
177
|
+
|
178
|
+
def with_file(&_block)
|
179
|
+
tmp_file = Tempfile.new('cure-temp')
|
180
|
+
tmp_file.write(@file_contents)
|
181
|
+
tmp_file.open
|
182
|
+
|
183
|
+
yield tmp_file, @ref_name
|
184
|
+
|
185
|
+
ensure
|
186
|
+
tmp_file.unlink
|
187
|
+
end
|
188
|
+
|
189
|
+
def description
|
190
|
+
"<content provided>"
|
191
|
+
end
|
192
|
+
end
|
55
193
|
end
|
56
194
|
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cure/log"
|
4
|
+
require "cure/config"
|
5
|
+
require "cure/database"
|
6
|
+
require "cure/helpers/file_helpers"
|
7
|
+
require "cure/helpers/perf_helpers"
|
8
|
+
|
9
|
+
require "cure/extract/extractor"
|
10
|
+
require "cure/transformation/transform"
|
11
|
+
require "cure/export/manager"
|
12
|
+
|
13
|
+
require "rcsv"
|
14
|
+
|
15
|
+
module Cure
|
16
|
+
# Coordinates the entire process:
|
17
|
+
# Extract -> Build -> Transform -> Export
|
18
|
+
class Coordinator
|
19
|
+
include Log
|
20
|
+
include Database
|
21
|
+
include Configuration
|
22
|
+
include Helpers::PerfHelpers
|
23
|
+
|
24
|
+
def process
|
25
|
+
# need to check config is init'd
|
26
|
+
result = nil
|
27
|
+
print_memory_usage do
|
28
|
+
print_time_spent do
|
29
|
+
# 1. Extract into SQLite
|
30
|
+
extract
|
31
|
+
# 2. Manipulate SQLite columns
|
32
|
+
build
|
33
|
+
# 3. Validate columns
|
34
|
+
|
35
|
+
# 4. Transform each row
|
36
|
+
with_tables.each do |table|
|
37
|
+
with_transformer(table) do |transformer|
|
38
|
+
with_exporters(table) do |exporters|
|
39
|
+
database_service.with_paged_result(table) do |row|
|
40
|
+
transformed_row = transformer.transform(row)
|
41
|
+
exporters.each do |exporter|
|
42
|
+
# 4. Export
|
43
|
+
exporter.process_row(transformed_row)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
result
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def extract
|
58
|
+
log_info "Beginning the extraction process..."
|
59
|
+
|
60
|
+
extractor = Extract::Extractor.new({})
|
61
|
+
|
62
|
+
file_count = 0
|
63
|
+
config.with_source_file do |file, ref_name|
|
64
|
+
extractor.parse_csv(file, ref_name: ref_name)
|
65
|
+
file_count += 1
|
66
|
+
end
|
67
|
+
|
68
|
+
log_info "...extraction complete"
|
69
|
+
end
|
70
|
+
|
71
|
+
def build
|
72
|
+
log_info "Beginning the building process..."
|
73
|
+
candidates = config.template.builder.candidates
|
74
|
+
candidates.each(&:perform)
|
75
|
+
|
76
|
+
log_info "... building complete"
|
77
|
+
end
|
78
|
+
|
79
|
+
# @yieldreturn [Cure::Transformation::Transform]
|
80
|
+
def with_transformer(named_range, &block)
|
81
|
+
raise "No block passed" unless block
|
82
|
+
|
83
|
+
log_info "Beginning the transformation process..."
|
84
|
+
candidates = config.template.transformations.candidates.select { |c| c.named_range == named_range.to_s }
|
85
|
+
transformer = Cure::Transformation::Transform.new(candidates)
|
86
|
+
yield transformer
|
87
|
+
|
88
|
+
log_info "...transform complete"
|
89
|
+
end
|
90
|
+
|
91
|
+
# @yieldreturn [Cure::Export::Section]
|
92
|
+
def with_exporters(named_range, &block)
|
93
|
+
raise "No block passed" unless block
|
94
|
+
|
95
|
+
log_info "Beginning export process..."
|
96
|
+
processors = config.template.exporters.processors.select { |c| c.named_range.to_s == named_range.to_s }
|
97
|
+
manager = Cure::Export::Manager.new(named_range, processors)
|
98
|
+
|
99
|
+
manager.with_processors(&block)
|
100
|
+
|
101
|
+
log_info "...export complete"
|
102
|
+
end
|
103
|
+
|
104
|
+
def with_tables
|
105
|
+
config.template.exporters.processors.map { |e| e.named_range.to_sym }.uniq
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "sequel"
|
4
|
+
require "sqlite3"
|
5
|
+
require "singleton"
|
6
|
+
|
7
|
+
require "cure/config"
|
8
|
+
|
9
|
+
module Cure
|
10
|
+
module Database
|
11
|
+
def database_service
|
12
|
+
database = DatabaseSource.instance.database_service
|
13
|
+
return database if database
|
14
|
+
|
15
|
+
init_database
|
16
|
+
|
17
|
+
DatabaseSource.instance.database_service
|
18
|
+
end
|
19
|
+
|
20
|
+
def init_database
|
21
|
+
DatabaseSource.instance.init_database
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class DatabaseSource
|
26
|
+
include Singleton
|
27
|
+
|
28
|
+
attr_reader :database_service
|
29
|
+
|
30
|
+
def init_database
|
31
|
+
@database_service = DatabaseService.new
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class DatabaseService
|
36
|
+
include Cure::Configuration
|
37
|
+
|
38
|
+
# @return [Sequel::SQLite::Database]
|
39
|
+
attr_reader :database
|
40
|
+
|
41
|
+
def initialize
|
42
|
+
@database = init_database
|
43
|
+
setup_db
|
44
|
+
end
|
45
|
+
|
46
|
+
def setup_db
|
47
|
+
# Load this from config defined by user?
|
48
|
+
@database.execute <<-SQL
|
49
|
+
PRAGMA journal_mode = OFF;
|
50
|
+
PRAGMA synchronous = 0;
|
51
|
+
PRAGMA cache_size = 1000000;
|
52
|
+
PRAGMA locking_mode = EXCLUSIVE;
|
53
|
+
PRAGMA temp_store = MEMORY;
|
54
|
+
SQL
|
55
|
+
end
|
56
|
+
|
57
|
+
# App Service calls
|
58
|
+
def find_variable(property_name)
|
59
|
+
@database.from(:variables).where(name: property_name).get(:value)
|
60
|
+
end
|
61
|
+
|
62
|
+
def find_translation(source_value)
|
63
|
+
@database.from(:translations).where(source_value: source_value).get(:value)
|
64
|
+
end
|
65
|
+
|
66
|
+
def all_translations
|
67
|
+
@database.from(:translations).all
|
68
|
+
end
|
69
|
+
|
70
|
+
# @param [Symbol,String] tbl_name
|
71
|
+
# @param [Array] columns
|
72
|
+
def create_table(tbl_name, columns, auto_increment: true)
|
73
|
+
tbl_name = tbl_name.to_sym if tbl_name.class != Symbol
|
74
|
+
|
75
|
+
@database.create_table tbl_name do
|
76
|
+
primary_key :_id, auto_increment: auto_increment
|
77
|
+
columns.each do |col_name|
|
78
|
+
column col_name.to_sym, String
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# @param [Symbol,String] tbl_name
|
84
|
+
def truncate_table(tbl_name)
|
85
|
+
@database[tbl_name.to_sym].truncate
|
86
|
+
end
|
87
|
+
|
88
|
+
# @param [Symbol,String] tbl_name
|
89
|
+
def table_count(tbl_name)
|
90
|
+
@database[tbl_name.to_sym].count
|
91
|
+
end
|
92
|
+
|
93
|
+
def with_transaction(&block)
|
94
|
+
@database.transaction({}, &block)
|
95
|
+
end
|
96
|
+
|
97
|
+
# @param [Symbol,String] tbl_name
|
98
|
+
# @return [TrueClass, FalseClass]
|
99
|
+
def table_exist?(tbl_name)
|
100
|
+
tbl_name = tbl_name.to_sym if tbl_name.class != Symbol
|
101
|
+
|
102
|
+
@database.table_exists?(tbl_name)
|
103
|
+
end
|
104
|
+
alias table_exists? table_exist?
|
105
|
+
|
106
|
+
# @param [Symbol,String] tbl_name
|
107
|
+
# @param [Array<String>] row
|
108
|
+
def insert_row(tbl_name, row)
|
109
|
+
@database[tbl_name.to_sym].insert(row)
|
110
|
+
end
|
111
|
+
|
112
|
+
# @param [Symbol,String] tbl_name
|
113
|
+
# @param [Array<String>] rows
|
114
|
+
def insert_batched_rows(tbl_name, rows)
|
115
|
+
@database[tbl_name.to_sym].import(@database[tbl_name.to_sym].columns, rows)
|
116
|
+
end
|
117
|
+
|
118
|
+
def add_column(tbl_name, new_column, default: "")
|
119
|
+
tbl_name = tbl_name.to_sym if tbl_name.class != Symbol
|
120
|
+
new_column = new_column.to_sym if new_column.class != Symbol
|
121
|
+
|
122
|
+
@database.add_column(tbl_name, new_column, String, default: default)
|
123
|
+
end
|
124
|
+
|
125
|
+
def remove_column(tbl_name, remove_column)
|
126
|
+
tbl_name = tbl_name.to_sym if tbl_name.class != Symbol
|
127
|
+
remove_column = remove_column.to_sym if remove_column.class != Symbol
|
128
|
+
|
129
|
+
@database.drop_column tbl_name, remove_column
|
130
|
+
end
|
131
|
+
|
132
|
+
def list_columns(tbl_name)
|
133
|
+
@database[tbl_name.to_sym].columns
|
134
|
+
end
|
135
|
+
|
136
|
+
def rename_column(tbl_name, old_column, new_column)
|
137
|
+
tbl_name = tbl_name.to_sym if tbl_name.class != Symbol
|
138
|
+
old_column = old_column.to_sym if old_column.class != Symbol
|
139
|
+
new_column = new_column.to_sym if new_column.class != Symbol
|
140
|
+
|
141
|
+
@database.rename_column tbl_name, old_column, new_column
|
142
|
+
end
|
143
|
+
|
144
|
+
def copy_column(tbl_name, from_column, to_column)
|
145
|
+
tbl_name = tbl_name.to_sym if tbl_name.class != Symbol
|
146
|
+
from_column = from_column.to_sym if from_column.class != Symbol
|
147
|
+
to_column = to_column.to_sym if to_column.class != Symbol
|
148
|
+
|
149
|
+
add_column tbl_name, to_column
|
150
|
+
run("UPDATE #{tbl_name} SET #{to_column} = #{from_column}")
|
151
|
+
end
|
152
|
+
|
153
|
+
def run(query, opts={})
|
154
|
+
@database.run(query, opts)
|
155
|
+
end
|
156
|
+
|
157
|
+
# Can we decouple query from named range? Probably more difficult
|
158
|
+
# than it seems. But would be nice to create two queries that doesn't
|
159
|
+
# require two tables (named ranges).
|
160
|
+
def with_paged_result(tbl_name, chunk_size: 100, &block)
|
161
|
+
raise "No block given" unless block
|
162
|
+
|
163
|
+
query = config.template.queries.find(tbl_name)
|
164
|
+
if query
|
165
|
+
@database[query.query].order(:_id).paged_each(rows_per_fetch: chunk_size, &block)
|
166
|
+
else
|
167
|
+
@database[tbl_name.to_sym].order(:_id).paged_each(rows_per_fetch: chunk_size, &block)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def list_tables
|
172
|
+
tbl_arr = @database.tables
|
173
|
+
tbl_arr.delete(:variables)
|
174
|
+
|
175
|
+
if tbl_arr.include?(:translations)
|
176
|
+
tbl_arr.delete(:translations)
|
177
|
+
tbl_arr.push(:translations)
|
178
|
+
end
|
179
|
+
|
180
|
+
tbl_arr
|
181
|
+
end
|
182
|
+
|
183
|
+
private
|
184
|
+
|
185
|
+
def init_database
|
186
|
+
# Build from config
|
187
|
+
# This must clean the database if its not in memory
|
188
|
+
Sequel.connect("sqlite:/")
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|