k_domain 0.0.1 → 0.0.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.
- checksums.yaml +4 -4
- data/Guardfile +30 -0
- data/Rakefile +0 -3
- data/STORIES.md +44 -0
- data/USAGE.md +19 -0
- data/lib/k_domain/raw_schema/template.rb +226 -0
- data/lib/k_domain/raw_schema/transform.rb +67 -0
- data/lib/k_domain/version.rb +1 -1
- data/lib/k_domain.rb +1 -2
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a69b94955a43f91d71e034b94a5caf0d3fdbb7b130c480d4eabc45818bbe2262
|
4
|
+
data.tar.gz: 0f4a126ee7c9bed5d99ac7449e5f97ec558030b126dbaa3c6194cdb5ce8430bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3a7a877e80c9f23cdb93ab7952e027bdddc89bd6e888212c6fc0622cc0d5ae6d9b73e3e9651949ecd1afa2f1323e6446045f579cb6e83c030c5d8d711979deaa
|
7
|
+
data.tar.gz: 434275e01ec2308d4a1407f5e8bd7ab9ec9708218510b6a719c144e2d9b6abb7e2caafa67efc617ac33295cd276c2326ccd961468ee4870b38266ef1b1295296
|
data/Guardfile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
guard :bundler, cmd: 'bundle install' do
|
4
|
+
watch('Gemfile')
|
5
|
+
watch('k_domain.gemspec')
|
6
|
+
end
|
7
|
+
|
8
|
+
group :green_pass_then_cop, halt_on_fail: true do
|
9
|
+
guard :rspec, cmd: 'bundle exec rspec -f doc' do
|
10
|
+
require 'guard/rspec/dsl'
|
11
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
12
|
+
|
13
|
+
# RSpec files
|
14
|
+
rspec = dsl.rspec
|
15
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
16
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
17
|
+
watch(rspec.spec_files)
|
18
|
+
|
19
|
+
# Ruby files
|
20
|
+
ruby = dsl.ruby
|
21
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
22
|
+
watch(%r{^lib/k_domain/**/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
|
23
|
+
watch(%r{^lib/k_domain/commands/(.+)\.rb$}) { |m| "spec/unit/commands/#{m[1]}_spec.rb" }
|
24
|
+
end
|
25
|
+
|
26
|
+
# guard :rubocop, all_on_start: false, cli: ['--format', 'clang'] do
|
27
|
+
# watch(/{.+\.rb$/)
|
28
|
+
# watch(%r{(?:.+/)?\.rubocop(?:_todo)?\.yml$}) { |m| File.dirname(m[0]) }
|
29
|
+
# end
|
30
|
+
end
|
data/Rakefile
CHANGED
data/STORIES.md
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# K Domain
|
2
|
+
|
3
|
+
> K Domain builds complex domain schemas by combining the database schema with a rich entity relationship DSLs
|
4
|
+
|
5
|
+
As an Application Developer, I need a rich and configurable ERD schema, so I can generate enterprise applications quickly
|
6
|
+
|
7
|
+
## Development radar
|
8
|
+
|
9
|
+
### Stories next on list
|
10
|
+
|
11
|
+
As a Developer, I can DO_SOMETHING, so that I QUALITY_OF_LIFE
|
12
|
+
|
13
|
+
- Subtask
|
14
|
+
|
15
|
+
## Stories and tasks
|
16
|
+
|
17
|
+
### Tasks - completed
|
18
|
+
|
19
|
+
Setup RubyGems and RubyDoc
|
20
|
+
|
21
|
+
- Build and deploy gem to [rubygems.org](https://rubygems.org/gems/k_domain)
|
22
|
+
- Attach documentation to [rubydoc.info](https://rubydoc.info/github/to-do-/k_domain/master)
|
23
|
+
|
24
|
+
Setup project management, requirement and SCRUM documents
|
25
|
+
|
26
|
+
- Setup readme file
|
27
|
+
- Setup user stories and tasks
|
28
|
+
- Setup a project backlog
|
29
|
+
- Setup an examples/usage document
|
30
|
+
|
31
|
+
Setup GitHub Action (test and lint)
|
32
|
+
|
33
|
+
- Setup Rspec action
|
34
|
+
- Setup RuboCop action
|
35
|
+
|
36
|
+
Setup new Ruby GEM
|
37
|
+
|
38
|
+
- Build out a standard GEM structure
|
39
|
+
- Add automated semantic versioning
|
40
|
+
- Add Rspec unit testing framework
|
41
|
+
- Add RuboCop linting
|
42
|
+
- Add Guard for automatic watch and test
|
43
|
+
- Add GitFlow support
|
44
|
+
- Add GitHub Repository
|
data/USAGE.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# K Domain
|
2
|
+
|
3
|
+
> K Domain builds complex domain schemas by combining the database schema with a rich entity relationship DSLs
|
4
|
+
|
5
|
+
As an Application Developer, I need a rich and configurable ERD schema, so I can generate enterprise applications quickly
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
### Sample Classes
|
10
|
+
|
11
|
+
#### Simple example
|
12
|
+
|
13
|
+
Description for a simple example that shows up in the USAGE.MD
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
class SomeRuby
|
17
|
+
def initialize; end
|
18
|
+
end
|
19
|
+
```
|
@@ -0,0 +1,226 @@
|
|
1
|
+
class LoadSchema
|
2
|
+
attr_reader :schema
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@unique_keys = {}
|
6
|
+
@current_table = nil
|
7
|
+
@rails_version = 4
|
8
|
+
@schema = {
|
9
|
+
tables: [],
|
10
|
+
foreign_keys: [],
|
11
|
+
indexes: [],
|
12
|
+
meta: {
|
13
|
+
rails: @rails_version,
|
14
|
+
database: {
|
15
|
+
type: 'postgres',
|
16
|
+
version: nil, # TODO
|
17
|
+
extensions: []
|
18
|
+
},
|
19
|
+
unique_keys: []
|
20
|
+
}
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
# ----------------------------------------------------------------------
|
25
|
+
# Inject start
|
26
|
+
# original file: {{source_file}}
|
27
|
+
# ----------------------------------------------------------------------
|
28
|
+
def load_schema
|
29
|
+
{{rails_schema}}
|
30
|
+
end
|
31
|
+
|
32
|
+
# ----------------------------------------------------------------------
|
33
|
+
# original file: {{source_file}}
|
34
|
+
# Inject end
|
35
|
+
# ----------------------------------------------------------------------
|
36
|
+
|
37
|
+
def write_json(file)
|
38
|
+
schema[:meta][:rails] = @rails_version
|
39
|
+
File.write(file, JSON.pretty_generate(schema))
|
40
|
+
end
|
41
|
+
|
42
|
+
# This is the rails timestamp and will be replaced by the action rails version
|
43
|
+
def load(version:)
|
44
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
45
|
+
# puts 'about to load'
|
46
|
+
yield if block_given?
|
47
|
+
|
48
|
+
schema[:meta][:rails] = @rails_version
|
49
|
+
|
50
|
+
sort
|
51
|
+
# code to time
|
52
|
+
|
53
|
+
# log.kv 'extensions', schema[:database][:extensions].length
|
54
|
+
# log.kv 'tables', schema[:tables].length
|
55
|
+
# log.kv 'indexes', schema[:indexes].length
|
56
|
+
# # a low foreign_keys count is indicative of not using SQL referential integrity
|
57
|
+
# log.kv 'foreign_keys', schema[:foreign_keys].length
|
58
|
+
# log.kv 'Time Taken', (finish - start)
|
59
|
+
|
60
|
+
# puts schema[:database][:extensions]
|
61
|
+
# print_unique_keys(type: :foreign_keys, title: 'unique options for foreign_keys')
|
62
|
+
# print_unique_keys(type: :columns, title: 'unique options for columns')
|
63
|
+
# print_unique_keys(type: :fields, category: :integer , title: 'unique options for column - integer')
|
64
|
+
# print_unique_keys(type: :fields, category: :decimal , title: 'unique options for column - decimal')
|
65
|
+
# print_unique_keys(type: :fields, category: :string , title: 'unique options for column - string')
|
66
|
+
# print_unique_keys(type: :fields, category: :datetime, title: 'unique options for column - datetime')
|
67
|
+
# print_unique_keys(type: :fields, category: :date , title: 'unique options for column - date')
|
68
|
+
# print_unique_keys(type: :fields, category: :text , title: 'unique options for column - text')
|
69
|
+
# print_unique_keys(type: :fields, category: :boolean , title: 'unique options for column - boolean')
|
70
|
+
# print_unique_keys(type: :fields, category: :jsonb , title: 'unique options for column - jsonb')
|
71
|
+
# print_unique_keys(type: :fields, category: :hstore , title: 'unique options for column - hstore')
|
72
|
+
# print_unique_keys(type: :fields, category: :float , title: 'unique options for column - float')
|
73
|
+
end
|
74
|
+
|
75
|
+
def enable_extension(name)
|
76
|
+
# puts "enable_extension(#{name})"
|
77
|
+
schema[:meta][:database][:extensions] << name
|
78
|
+
end
|
79
|
+
|
80
|
+
def create_table(name, **opts)
|
81
|
+
id = opts[:id]
|
82
|
+
primary_key = opts[:primary_key] || (id == false ? nil : "id")
|
83
|
+
primary_key_type = if id == false
|
84
|
+
nil
|
85
|
+
elsif id.nil?
|
86
|
+
"bigint"
|
87
|
+
else
|
88
|
+
id
|
89
|
+
end
|
90
|
+
|
91
|
+
@current_table = {
|
92
|
+
name: name,
|
93
|
+
primary_key: primary_key, # infer the actual value that should be in the database
|
94
|
+
primary_key_type: primary_key_type, # infer the actual value that should be in the database
|
95
|
+
columns: [],
|
96
|
+
indexes: [],
|
97
|
+
rails_schema: { # as reported by the rails schema
|
98
|
+
primary_key: opts[:primary_key],
|
99
|
+
id: id,
|
100
|
+
force: opts[:force]
|
101
|
+
}
|
102
|
+
}
|
103
|
+
# schema[:tables][name] = @current_table
|
104
|
+
schema[:tables] << @current_table
|
105
|
+
|
106
|
+
yield(self) if block_given?
|
107
|
+
end
|
108
|
+
|
109
|
+
def add_field(name, type, **opts)
|
110
|
+
# puts "add_field(#{name}, #{type})"
|
111
|
+
row = { name: name, type: type, **opts }
|
112
|
+
@current_table[:columns] << row
|
113
|
+
|
114
|
+
add_unique_keys(row.keys, type: :columns)
|
115
|
+
add_unique_keys(row.keys, type: :fields, category: type)
|
116
|
+
end
|
117
|
+
|
118
|
+
def add_index(name, fields, **opts)
|
119
|
+
# puts "add_index(#{name})"
|
120
|
+
row = { name: name, fields: fields, **opts }
|
121
|
+
@current_table[:indexes] << row
|
122
|
+
schema[:indexes] << row
|
123
|
+
add_unique_keys(row.keys, type: :indexes)
|
124
|
+
end
|
125
|
+
|
126
|
+
# This method was introduced onto the schema in rails 5
|
127
|
+
def index(fields, **opts)
|
128
|
+
@rails_version = 5
|
129
|
+
name = opts[:name]
|
130
|
+
opts.delete(:name)
|
131
|
+
add_index(name, fields, **opts)
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
def add_foreign_key(left_table, right_table, **opts)
|
136
|
+
# puts "add_foreign_key(#{left_table}, #{right_table})"
|
137
|
+
row = { left: left_table, right: right_table, **opts }
|
138
|
+
schema[:foreign_keys] << row
|
139
|
+
add_unique_keys(row.keys, type: :foreign_keys)
|
140
|
+
end
|
141
|
+
|
142
|
+
def add_unique_keys(keys, type:, category: nil)
|
143
|
+
key = [type, category, keys.join('-')].compact.join('|')
|
144
|
+
return if @unique_keys.key?(key)
|
145
|
+
|
146
|
+
@unique_keys[key] = key
|
147
|
+
schema[:meta][:unique_keys] << { type: type, category: category, key: keys.join(','), keys: keys }
|
148
|
+
end
|
149
|
+
|
150
|
+
def print_unique_keys(type:, category: nil, title: )
|
151
|
+
log.section_heading(title)
|
152
|
+
|
153
|
+
filter_key_infos = schema[:meta][:unique_keys].select { |key_info| key_info[:type] == type && (category.nil? || key_info[:category] == category) }
|
154
|
+
|
155
|
+
# log.kv 'all', filter_key_infos.flat_map { |key_info| key_info[:keys] }.uniq, 50
|
156
|
+
|
157
|
+
filter_key_infos.each do |key_info|
|
158
|
+
log.kv key_info[:key], key_info[:keys], 50
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def integer(name, **opts)
|
163
|
+
add_field(name, :integer, **opts)
|
164
|
+
end
|
165
|
+
|
166
|
+
def bigint(name, **opts)
|
167
|
+
add_field(name, :bigint, **opts)
|
168
|
+
end
|
169
|
+
|
170
|
+
def decimal(name, **opts)
|
171
|
+
add_field(name, :decimal, **opts)
|
172
|
+
end
|
173
|
+
|
174
|
+
def string(name, **opts)
|
175
|
+
add_field(name, :string, **opts)
|
176
|
+
end
|
177
|
+
|
178
|
+
def datetime(name, **opts)
|
179
|
+
add_field(name, :datetime, **opts)
|
180
|
+
end
|
181
|
+
|
182
|
+
def date(name, **opts)
|
183
|
+
add_field(name, :date, **opts)
|
184
|
+
end
|
185
|
+
|
186
|
+
def text(name, **opts)
|
187
|
+
add_field(name, :text, **opts)
|
188
|
+
end
|
189
|
+
|
190
|
+
def boolean(name, **opts)
|
191
|
+
add_field(name, :boolean, **opts)
|
192
|
+
end
|
193
|
+
|
194
|
+
def jsonb(name, **opts)
|
195
|
+
add_field(name, :jsonb, **opts)
|
196
|
+
end
|
197
|
+
|
198
|
+
def hstore(name, **opts)
|
199
|
+
add_field(name, :hstore, **opts)
|
200
|
+
end
|
201
|
+
|
202
|
+
def float(name, **opts)
|
203
|
+
add_field(name, :float, **opts)
|
204
|
+
end
|
205
|
+
|
206
|
+
def sort
|
207
|
+
schema[:indexes].sort_by! { |i| i[:name] }
|
208
|
+
schema[:tables].each { |table| table[:indexes].sort_by! { |i| i[:name] } }
|
209
|
+
|
210
|
+
# Insert a key that represents all unique keys, and then sort
|
211
|
+
unique_keys_per_group = schema[:meta][:unique_keys]
|
212
|
+
.group_by { |key_info| [key_info[:type], key_info[:category]] }
|
213
|
+
.map do |group, values|
|
214
|
+
all_keys = values.flat_map { |key_info| key_info[:keys] }.uniq
|
215
|
+
{
|
216
|
+
type: group[0],
|
217
|
+
category: group[01],
|
218
|
+
key: 'all',
|
219
|
+
keys: all_keys
|
220
|
+
}
|
221
|
+
end
|
222
|
+
|
223
|
+
schema[:meta][:unique_keys].concat(unique_keys_per_group)
|
224
|
+
schema[:meta][:unique_keys].sort! { |a,b| ([a[:type], a[:category],a[:key]] <=> [b[:type], b[:category],b[:key]]) }
|
225
|
+
end
|
226
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# Annotates the original schema with methods that implement existing method calls
|
2
|
+
# that are already in the schema so that we can build a hash.
|
3
|
+
#
|
4
|
+
# Writes a new annotated schema.rb file with a public method called load that
|
5
|
+
# builds the hash
|
6
|
+
# frozen_string_literal: true
|
7
|
+
|
8
|
+
module KDomain
|
9
|
+
module RawSchema
|
10
|
+
class Transform
|
11
|
+
include KLog::Logging
|
12
|
+
|
13
|
+
attr_reader :source_file
|
14
|
+
attr_reader :template_file
|
15
|
+
attr_reader :target_ruby_class
|
16
|
+
|
17
|
+
def initialize(source_file)#, target_file)
|
18
|
+
@source_file = source_file
|
19
|
+
@template_file = 'lib/k_domain/raw_schema/template.rb'
|
20
|
+
end
|
21
|
+
|
22
|
+
def call
|
23
|
+
# log.kv 'source_file', source_file
|
24
|
+
# log.kv 'template_file', template_file
|
25
|
+
# log.kv 'source_file?', File.exist?(source_file)
|
26
|
+
# log.kv 'template_file?', File.exist?(template_file)
|
27
|
+
|
28
|
+
log.error "Template not found: #{template_file}" unless File.exist?(template_file)
|
29
|
+
|
30
|
+
content = File.read(source_file)
|
31
|
+
content
|
32
|
+
.gsub!(/ActiveRecord::Schema.define/, 'load')
|
33
|
+
|
34
|
+
lines = content.lines.map { |line| " #{line}" }.join()
|
35
|
+
|
36
|
+
@target_ruby_class = File
|
37
|
+
.read(template_file)
|
38
|
+
.gsub('{{source_file}}', source_file)
|
39
|
+
.gsub('{{rails_schema}}', lines)
|
40
|
+
end
|
41
|
+
|
42
|
+
def write_target(target_file)
|
43
|
+
if target_ruby_class.nil?
|
44
|
+
puts '.call method has not been executed'
|
45
|
+
return
|
46
|
+
end
|
47
|
+
|
48
|
+
FileUtils.mkdir_p(File.dirname(target_file))
|
49
|
+
File.write(target_file, target_ruby_class)
|
50
|
+
end
|
51
|
+
|
52
|
+
def json
|
53
|
+
if target_ruby_class.nil?
|
54
|
+
puts '.call method has not been executed'
|
55
|
+
return
|
56
|
+
end
|
57
|
+
|
58
|
+
# load target_file
|
59
|
+
eval target_ruby_class
|
60
|
+
|
61
|
+
loader = LoadSchema.new
|
62
|
+
loader.load_schema()
|
63
|
+
loader.schema
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/k_domain/version.rb
CHANGED
data/lib/k_domain.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: k_domain
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Cruwys
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-09-
|
11
|
+
date: 2021-09-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: k_log
|
@@ -38,9 +38,12 @@ files:
|
|
38
38
|
- ".rubocop.yml"
|
39
39
|
- CODE_OF_CONDUCT.md
|
40
40
|
- Gemfile
|
41
|
+
- Guardfile
|
41
42
|
- LICENSE.txt
|
42
43
|
- README.md
|
43
44
|
- Rakefile
|
45
|
+
- STORIES.md
|
46
|
+
- USAGE.md
|
44
47
|
- bin/console
|
45
48
|
- bin/k
|
46
49
|
- bin/kgitsync
|
@@ -50,6 +53,8 @@ files:
|
|
50
53
|
- hooks/update-version
|
51
54
|
- k_domain.gemspec
|
52
55
|
- lib/k_domain.rb
|
56
|
+
- lib/k_domain/raw_schema/template.rb
|
57
|
+
- lib/k_domain/raw_schema/transform.rb
|
53
58
|
- lib/k_domain/version.rb
|
54
59
|
homepage: http://appydave.com/gems/k-domain
|
55
60
|
licenses:
|