fixture_fox 0.1.1

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.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.rspec +3 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +6 -0
  6. data/Gemfile +7 -0
  7. data/README.md +36 -0
  8. data/Rakefile +6 -0
  9. data/TODO +79 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/doc/diagram.drawio +1 -0
  13. data/examples/1-1-subrecord.fox +15 -0
  14. data/examples/1-1-subrecord.sql +22 -0
  15. data/examples/N-M.fox +25 -0
  16. data/examples/N-M.sql +34 -0
  17. data/examples/anchors.sql +13 -0
  18. data/examples/anchors.yml +4 -0
  19. data/examples/anchors1.fox +4 -0
  20. data/examples/anchors2.fox +6 -0
  21. data/examples/array.fox +84 -0
  22. data/examples/array.sql +53 -0
  23. data/examples/base.fox +81 -0
  24. data/examples/base.sql +52 -0
  25. data/examples/empty.fox +8 -0
  26. data/examples/empty.sql +33 -0
  27. data/examples/include/schema-included.fox +23 -0
  28. data/examples/inherit.fox +26 -0
  29. data/examples/inherit.sql +28 -0
  30. data/examples/kind.fox +17 -0
  31. data/examples/kind.sql +31 -0
  32. data/examples/link.fox +35 -0
  33. data/examples/link.sql +32 -0
  34. data/examples/root.fox +22 -0
  35. data/examples/schema-fragment-1.fox +9 -0
  36. data/examples/schema-fragment-2.fox +21 -0
  37. data/examples/schema-include.fox +10 -0
  38. data/examples/schema-indent.fox +29 -0
  39. data/examples/schema.fox +29 -0
  40. data/examples/schema.sql +33 -0
  41. data/examples/types.fox +8 -0
  42. data/examples/types.sql +17 -0
  43. data/examples/views.fox +15 -0
  44. data/examples/views.sql +38 -0
  45. data/exe/fox +178 -0
  46. data/fixture_fox.gemspec +37 -0
  47. data/lib/fixture_fox/analyzer.rb +371 -0
  48. data/lib/fixture_fox/anchor.rb +93 -0
  49. data/lib/fixture_fox/ast.rb +176 -0
  50. data/lib/fixture_fox/error.rb +23 -0
  51. data/lib/fixture_fox/hash_parser.rb +111 -0
  52. data/lib/fixture_fox/idr.rb +62 -0
  53. data/lib/fixture_fox/line.rb +217 -0
  54. data/lib/fixture_fox/parser.rb +153 -0
  55. data/lib/fixture_fox/token.rb +173 -0
  56. data/lib/fixture_fox/tokenizer.rb +78 -0
  57. data/lib/fixture_fox/version.rb +3 -0
  58. data/lib/fixture_fox.rb +216 -0
  59. metadata +227 -0
@@ -0,0 +1,26 @@
1
+
2
+ accounts
3
+ - &alice
4
+ i: 1
5
+ user:
6
+ name: 'alice'
7
+ - &bob
8
+ i: 2
9
+ user:
10
+ name: 'bob'
11
+ - user:
12
+ name: 'cloe'
13
+
14
+ jobs
15
+ - name: 'bobjob' # her
16
+ - name: 'alicejob'
17
+ account:
18
+ - i: 3
19
+ user:
20
+ name: 'dan'
21
+
22
+ infos
23
+ - &bla_info
24
+ info: "Bla bla"
25
+ job: # Her
26
+ name: 'info job'
@@ -0,0 +1,28 @@
1
+
2
+ \connect postgres
3
+
4
+ drop database if exists fox;
5
+ create database fox;
6
+
7
+ \connect fox
8
+
9
+ create unlogged table accounts (
10
+ id integer generated by default as identity primary key,
11
+ i integer
12
+ );
13
+
14
+ create unlogged table users (
15
+ id integer primary key references accounts(id),
16
+ name text not null
17
+ );
18
+
19
+ create unlogged table jobs (
20
+ id integer primary key references accounts(id),
21
+ name text not null
22
+ );
23
+
24
+ create unlogged table infos (
25
+ id integer generated by default as identity primary key,
26
+ job_id integer references jobs(id),
27
+ info text not null
28
+ );
data/examples/kind.fox ADDED
@@ -0,0 +1,17 @@
1
+
2
+ user_groups
3
+ - name: all
4
+ - name: intern
5
+
6
+ user_roles
7
+ - name: sysadm
8
+ - name: user
9
+
10
+ users
11
+ - username: Alice
12
+ emails:
13
+ - email: "alice@restaurant.com"
14
+ user_group: all
15
+ user_role: user
16
+
17
+
data/examples/kind.sql ADDED
@@ -0,0 +1,31 @@
1
+
2
+ \connect postgres
3
+
4
+ drop database if exists fox;
5
+ create database fox;
6
+
7
+ \connect fox
8
+
9
+ create table user_groups (
10
+ id integer not null generated by default as identity primary key,
11
+ name varchar not null unique
12
+ );
13
+
14
+ create table user_roles (
15
+ id integer not null generated by default as identity primary key,
16
+ name varchar not null unique
17
+ );
18
+
19
+ create table users (
20
+ id integer not null generated by default as identity primary key,
21
+ username varchar not null,
22
+ user_role_kind varchar not null references user_roles(name),
23
+ user_group varchar not null references user_groups(name)
24
+ );
25
+
26
+ create table emails (
27
+ id integer not null generated by default as identity primary key,
28
+ user_id integer not null references users(id),
29
+ email varchar not null
30
+ );
31
+
data/examples/link.fox ADDED
@@ -0,0 +1,35 @@
1
+
2
+ #alice: Users
3
+ # name: Alice
4
+ #__END__
5
+
6
+ alice: Users
7
+ name: "Alice"
8
+ roles:
9
+ - &sysadm
10
+ name: Sysadm
11
+ parent_role: *user
12
+ picture:
13
+ file: "pretty.jpg"
14
+
15
+ roles
16
+ - &user
17
+ name: User
18
+ users:
19
+ - *bob
20
+ - &clara
21
+ name: Clara
22
+
23
+ bob: Users
24
+ name: Bob
25
+ picture:
26
+ file: "bob.jpg"
27
+
28
+ pictures
29
+ - user:
30
+ name: Dan
31
+ file: "dan.jpg"
32
+
33
+
34
+
35
+
data/examples/link.sql ADDED
@@ -0,0 +1,32 @@
1
+
2
+ \connect postgres
3
+
4
+ drop database if exists fox;
5
+ create database fox;
6
+
7
+ \connect fox
8
+
9
+ create table users (
10
+ id integer generated by default as identity primary key,
11
+ name varchar not null
12
+ );
13
+
14
+ create table pictures (
15
+ id integer generated by default as identity primary key,
16
+ user_id integer not null references users(id) unique,
17
+ file text not null
18
+ );
19
+
20
+ create table roles (
21
+ id integer generated by default as identity primary key,
22
+ parent_role_id integer references roles(id),
23
+ name varchar not null
24
+ );
25
+
26
+ create table user_roles (
27
+ id integer generated by default as identity primary key,
28
+ user_id integer not null references users(id),
29
+ role_id integer not null references roles(id)
30
+ );
31
+
32
+
data/examples/root.fox ADDED
@@ -0,0 +1,22 @@
1
+
2
+ # Not implemented
3
+
4
+ *alice.age: 42
5
+
6
+ *alice.roles:
7
+ - *sysadm
8
+ - *user
9
+ - &test
10
+ name: test
11
+
12
+ alice: Users
13
+ name: Alice
14
+ age: 20
15
+ roles:
16
+ - &admin
17
+ name: Administrator
18
+ - &sysadm
19
+ name: System administrator
20
+ - &user
21
+ name: User
22
+ - *test
@@ -0,0 +1,9 @@
1
+
2
+ @schema user_schema
3
+
4
+ role_schema.roles
5
+ - &sysadm_role
6
+ name: Sysadm
7
+ - &admin_role
8
+ name: Admin
9
+
@@ -0,0 +1,21 @@
1
+
2
+ posts
3
+ - &alices_first_post
4
+ title: "FIRST"
5
+
6
+ @schema user_schema
7
+
8
+ users
9
+ - name: Alice
10
+ roles:
11
+ - *sysadm_role
12
+ - &user_role
13
+ name: User
14
+ posts:
15
+ - *alices_first_post
16
+ - title: "SECOND"
17
+ - name: Bob
18
+ roles:
19
+ - *admin_role
20
+ - *user_role
21
+
@@ -0,0 +1,10 @@
1
+
2
+ @schema user_schema
3
+
4
+ role_schema.roles
5
+ - &sysadm_role
6
+ name: Sysadm
7
+ - &admin_role
8
+ name: Admin
9
+
10
+ @include include/schema-included.fox
@@ -0,0 +1,29 @@
1
+
2
+ @schema user_schema
3
+
4
+ role_schema.roles
5
+ - &sysadm_role
6
+ name: Sysadm
7
+ - &admin_role
8
+ name: Admin
9
+
10
+ public.posts
11
+ - &alices_first_post
12
+ title: "FIRST"
13
+
14
+ @schema user_schema
15
+
16
+ users
17
+ - name: Alice
18
+ roles:
19
+ - *sysadm_role
20
+ - &user_role
21
+ name: User
22
+ posts:
23
+ - *alices_first_post
24
+ - title: "SECOND"
25
+ - name: Bob
26
+ roles:
27
+ - *admin_role
28
+ - *user_role
29
+
@@ -0,0 +1,29 @@
1
+
2
+ @schema user_schema
3
+
4
+ role_schema.roles
5
+ - &sysadm_role
6
+ name: Sysadm
7
+ - &admin_role
8
+ name: Admin
9
+
10
+ public.posts
11
+ - &alices_first_post
12
+ title: "FIRST"
13
+
14
+ @schema user_schema
15
+
16
+ users
17
+ - name: Alice
18
+ roles:
19
+ - *sysadm_role
20
+ - &user_role
21
+ name: User
22
+ posts:
23
+ - *alices_first_post
24
+ - title: "SECOND"
25
+ - name: Bob
26
+ roles:
27
+ - *admin_role
28
+ - *user_role
29
+
@@ -0,0 +1,33 @@
1
+
2
+ \connect postgres
3
+
4
+ drop database if exists fox;
5
+ create database fox;
6
+
7
+ \connect fox
8
+
9
+ create schema user_schema;
10
+ create schema role_schema;
11
+
12
+ create table role_schema.roles (
13
+ id integer generated by default as identity primary key,
14
+ name text not null
15
+ );
16
+
17
+ create table user_schema.users (
18
+ id integer generated by default as identity primary key,
19
+ name text not null
20
+ );
21
+
22
+ create table public.user_roles (
23
+ id integer generated by default as identity primary key,
24
+ role_id integer not null references role_schema.roles(id),
25
+ user_id integer not null references user_schema.users(id)
26
+ );
27
+
28
+ create table public.posts (
29
+ id integer generated by default as identity primary key,
30
+ user_id integer not null references user_schema.users(id),
31
+ title text not null
32
+ );
33
+
@@ -0,0 +1,8 @@
1
+
2
+ Users
3
+ - &record
4
+ f_text: "string"
5
+ f_integer: 42
6
+ f_float: 4.2
7
+ f_boolean: true
8
+ f_timestamp: '2020-01-01'
@@ -0,0 +1,17 @@
1
+
2
+ \connect postgres
3
+
4
+ drop database if exists fox;
5
+ create database fox;
6
+
7
+ \connect fox
8
+
9
+ create table users (
10
+ id integer generated by default as identity primary key,
11
+ f_text varchar,
12
+ f_integer integer,
13
+ f_float float,
14
+ f_boolean boolean,
15
+ f_timestamp timestamp without time zone default (now() at time zone 'UTC')
16
+ );
17
+
@@ -0,0 +1,15 @@
1
+
2
+ Users
3
+ - &alice
4
+ name: Alice
5
+ orders:
6
+ - item: Bread
7
+ amount: 10
8
+ - item: Coffee
9
+ amount: 5
10
+ - &bob
11
+ name: Bob
12
+ orders:
13
+ - item: Tomato
14
+ amount: 2
15
+
@@ -0,0 +1,38 @@
1
+
2
+ \connect postgres
3
+
4
+ drop database if exists fox;
5
+ create database fox;
6
+
7
+ \connect fox
8
+
9
+ create table users (
10
+ id integer generated by default as identity primary key,
11
+ name text not null
12
+ );
13
+
14
+ create table orders (
15
+ id integer generated by default as identity primary key,
16
+ item text not null,
17
+ amount integer not null default 0
18
+ );
19
+
20
+ create table user_orders (
21
+ id integer generated by default as identity primary key,
22
+ user_id integer not null references users(id),
23
+ order_id integer not null references orders(id)
24
+ );
25
+
26
+ create materialized view order_sums as
27
+ select u.id,
28
+ u.name,
29
+ sum(o.amount)
30
+ from users u
31
+ join user_orders uo on uo.user_id = u.id
32
+ join orders o on o.id = uo.order_id
33
+ group by
34
+ u.id,
35
+ u.name
36
+ ;
37
+
38
+
data/exe/fox ADDED
@@ -0,0 +1,178 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $t0 = Time.now
4
+
5
+ require 'fileutils'
6
+
7
+ require 'fixture_fox.rb'
8
+ require 'shellopts'
9
+ require "pg_graph/timer.rb"
10
+
11
+ include ShellOpts
12
+
13
+ SPEC = %(
14
+ @ Generate test data for Postgres databases
15
+
16
+ -r,reflections=EFILE
17
+ Reflections YML file
18
+
19
+ -s,state=EFILE?
20
+ State file. fox(1) reads ids and anchors from this file. Default is
21
+ fox.yml
22
+
23
+ -w,write=FILE? @ Write new state file
24
+ Write the new state to FILE or back to the input state file if FILE is
25
+ not given
26
+
27
+ -d,delete=KIND:touched,recursive,all,none
28
+ Controls the delete statements. 'touched' deletes only from tables that
29
+ are specified in the fox file, 'recursive' deletes from touched tables
30
+ and their dependent tables, and 'all' (the default) deletes from all
31
+ tables in the database. If 'none' is given, no delete statements are
32
+ emitted. Records included in the state file are never deleted
33
+
34
+ -f,format=FORMAT:sql,exec,psql,yaml
35
+ Controls the format of the 'data' output type. FORMAT can be one of
36
+ 'sql', 'exec', 'psql' (the default), or 'yaml'. The format option can
37
+ also be used together with the --dump option for some of the dump types
38
+
39
+ -x,exec
40
+ Execute the generated SQL instead of printing it on standard output
41
+
42
+ -t,time
43
+ Emit timings for sub-processes
44
+
45
+ --dump=KIND:tokens,meta,type,ast,idr,state,anchors,ids
46
+ Dump the given data on standard output. KIND can be one of 'tokens',
47
+ 'meta', 'type', 'ast', 'idr', 'state', 'anchors', or 'ids'
48
+
49
+ -- DATABASE [FOX-FILE...]
50
+ )
51
+
52
+ DEFAULT_STATE_FILE = "fox-state.fox"
53
+
54
+ class Break < RuntimeError; end
55
+
56
+ begin
57
+ opts, args = ShellOpts.process(SPEC, ARGV, exception: true)
58
+
59
+ timing = opts.time?
60
+ timer = Timer:: Timer.new
61
+
62
+ opts.format ||= 'psql'
63
+
64
+ if opts.state?
65
+ opts.state = File.expand_path(opts.state || DEFAULT_STATE_FILE)
66
+ FileUtils.touch(opts.state) if !File.exist?(opts.state)
67
+ end
68
+
69
+ db = args.extract(1)
70
+ files = args.empty? ? %w(/dev/stdin) : args
71
+ files.each { |path| File.readable?(path) or raise ShellOpts::Error.new(nil), "Can't read '#{path}'" }
72
+
73
+ tg = timer.group("initialization")
74
+
75
+ # Connect to postgres
76
+ begin
77
+ conn = tg.time("connect") { PgConn.new(db) }
78
+ rescue PG::ConnectionBad
79
+ raise ShellOpts::Error.new(nil), "Can't connect to database '#{db}'"
80
+ end
81
+
82
+ # Load meta object
83
+ meta = tg.time("meta") { PgMeta.new(conn) }
84
+ if opts.dump == "meta"
85
+ if opts.format == "yaml"
86
+ puts meta.to_yaml
87
+ else
88
+ meta.dump
89
+ end
90
+ exit
91
+ end
92
+
93
+ # Load reflections
94
+ if opts.reflections?
95
+ reflector = tg.time("reflections") { PgGraph::Reflector.load_yaml(YAML.load(File.read(opts.reflections))) }
96
+ else
97
+ reflector = tg.time("reflections") { PgGraph::Reflector.new }
98
+ end
99
+
100
+ # Dump types
101
+ type = tg.time("type") { PgGraph::Type.new(meta, reflector) }
102
+ if opts.dump == "type"
103
+ type.dump
104
+ exit
105
+ end
106
+
107
+ # Load state
108
+ tg = timer.group("compiling")
109
+ ids, anchors, fox = nil, nil, nil # Enter into namespace
110
+ tg.time("state") {
111
+ ids, anchors = opts.state? ? FixtureFox::Fox.read_state(type, opts.state) : [nil, nil]
112
+ }
113
+
114
+ # Create fox object
115
+ tg.time("initialize") {
116
+ fox = FixtureFox::Fox.new(type, ids: ids, anchors: anchors)
117
+ }
118
+ for file in files
119
+ source, lines = tg.time("tokenize") { fox.tokenize(file) }
120
+ tg.time("parse") { fox.parse(source, lines) }
121
+ end
122
+
123
+ if opts.dump == "tokens"
124
+ fox.lines.each { |l| puts l.to_s(long: true) }
125
+ exit
126
+ end
127
+
128
+ # Analyze
129
+ tga = tg.group("analyze")
130
+ tga.time("assign types") { fox.assign_types }
131
+ tga.time("check types") { fox.check_types }
132
+ tg.time("generate") { fox.generate }
133
+
134
+ if opts.dump?
135
+ case opts.dump || "data"
136
+ # when "tokens"; # Handled above
137
+ # when "meta"; # Handled above
138
+ # when "type"; # Handled above
139
+ when "ast"; fox.ast.dump
140
+ when "state"; FixtureFox::Fox.write_state(ids, anchors, "/dev/stdout")
141
+ when "idr"; fox.idr.dump
142
+ when "anchors"; FixtureFox::Fox.write_anchors(fox.anchors, "/dev/stdout")
143
+ when "ids"; FixtureFox::Fox.write_ids(fox.ids, "/dev/stdout")
144
+ end
145
+ exit
146
+ end
147
+
148
+ if opts.exec?
149
+ tg.time("write") { fox.data.write(conn, ids: ids) }
150
+ else
151
+ tg.time("emit") {
152
+ # puts "Format: #{opts.format.inspect}"
153
+ # exit
154
+ if opts.format == "yaml"
155
+ puts fox.to_yaml
156
+ else
157
+ puts fox.to_sql(format: opts.format&.to_sym || :psql, ids: ids, delete: opts.delete&.to_sym || :all)
158
+ end
159
+ }
160
+ end
161
+
162
+ # Save state
163
+ if opts.write?
164
+ FixtureFox::Fox.write_state(fox.ids, fox.anchors, opts.write || opts.state)
165
+ end
166
+
167
+ rescue FixtureFox::Error => ex
168
+ $stderr.puts ex.backtrace.join("\n")
169
+ $stderr.puts
170
+ $stderr.puts ex.error_message
171
+ $stderr.puts
172
+ $error = true
173
+ exit 1
174
+
175
+ ensure
176
+ timer.dump($stderr) if timing && !$error
177
+ end
178
+
@@ -0,0 +1,37 @@
1
+ require_relative 'lib/fixture_fox/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "fixture_fox"
5
+ spec.version = FixtureFox::VERSION
6
+ spec.authors = ["Claus Rasmussen"]
7
+ spec.email = ["claus.l.rasmussen@gmail.com"]
8
+
9
+ spec.summary = %q{fixture_fox gem}
10
+ spec.description = %q{fixture_fox gem}
11
+ spec.homepage = "http://www.nowhere.com/"
12
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
13
+
14
+
15
+ spec.metadata["homepage_uri"] = spec.homepage
16
+
17
+ # Specify which files should be added to the gem when it is released.
18
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
20
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ end
22
+ spec.bindir = "exe"
23
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ["lib"]
25
+
26
+ spec.add_dependency "pg"
27
+ spec.add_dependency "dry-inflector"
28
+ spec.add_dependency "indented_io"
29
+ spec.add_dependency "boolean"
30
+ spec.add_dependency "developer_exceptions"
31
+ spec.add_dependency "constrain"
32
+
33
+ spec.add_dependency "shellopts", "2.0.6"
34
+ spec.add_dependency "pg_graph", "0.1.0"
35
+
36
+ spec.add_development_dependency "simplecov"
37
+ end