prick 0.19.0 → 0.20.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 (112) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +10 -4
  3. data/README.md +7 -7
  4. data/Rakefile +3 -1
  5. data/TODO +13 -11
  6. data/bin/console +2 -1
  7. data/doc/build-yml.txt +14 -0
  8. data/exe/prick +237 -19
  9. data/lib/builder/batch.rb +147 -0
  10. data/lib/builder/builder.rb +122 -0
  11. data/lib/builder/node.rb +189 -0
  12. data/lib/builder/node_pool.rb +105 -0
  13. data/lib/builder/parser.rb +120 -0
  14. data/lib/local/command.rb +193 -0
  15. data/lib/{prick → local}/git.rb +148 -22
  16. data/lib/local/timer.rb +98 -0
  17. data/lib/prick/constants.rb +54 -66
  18. data/lib/prick/diff.rb +28 -18
  19. data/lib/prick/prick_version.rb +161 -0
  20. data/lib/prick/state.rb +80 -165
  21. data/lib/prick/version.rb +2 -163
  22. data/lib/prick.rb +38 -24
  23. data/lib/share/init/.gitignore +10 -0
  24. data/lib/share/init/.prick-context +2 -0
  25. data/lib/share/init/.rspec +3 -0
  26. data/{share/schema/schema/public → lib/share/init/migration}/.keep +0 -0
  27. data/lib/share/init/prick.yml +6 -0
  28. data/lib/share/init/schema/.keep +0 -0
  29. data/lib/share/init/schema/build.yml +2 -0
  30. data/lib/share/init/schema/prick/.keep +0 -0
  31. data/lib/share/init/schema/prick/build.yml +5 -0
  32. data/lib/share/init/schema/prick/data.sql +6 -0
  33. data/{share/schema → lib/share/init}/schema/prick/tables.sql +2 -3
  34. data/lib/share/init/schema/public/.keep +0 -0
  35. data/lib/share/init/spec/prick_helper.rb +1 -0
  36. data/lib/share/init/spec/prick_spec.rb +6 -0
  37. data/lib/share/init/spec/spec_helper.rb +50 -0
  38. data/lib/share/migrate/migration/build.yml +4 -0
  39. data/lib/share/migrate/migration/diff.after-tables.sql +0 -0
  40. data/lib/share/migrate/migration/diff.before-tables.sql +0 -0
  41. data/lib/share/migrate/migration/diff.tables.sql +0 -0
  42. data/lib/subcommand/prick-build.rb +55 -0
  43. data/lib/subcommand/prick-create.rb +78 -0
  44. data/lib/subcommand/prick-drop.rb +25 -0
  45. data/lib/subcommand/prick-fox.rb +62 -0
  46. data/lib/subcommand/prick-init.rb +46 -0
  47. data/lib/subcommand/prick-make.rb +202 -0
  48. data/lib/subcommand/prick-migrate.rb +37 -0
  49. data/lib/subcommand/prick-release.rb +23 -0
  50. data/lib/subcommand/prick-setup.rb +20 -0
  51. data/lib/subcommand/prick-teardown.rb +18 -0
  52. data/prick.gemspec +32 -21
  53. metadata +95 -76
  54. data/.gitignore +0 -29
  55. data/.travis.yml +0 -7
  56. data/doc/create_release.txt +0 -17
  57. data/doc/flow.txt +0 -98
  58. data/doc/migra +0 -1
  59. data/doc/migrations.txt +0 -172
  60. data/doc/notes.txt +0 -116
  61. data/doc/prick.txt +0 -114
  62. data/doc/sh.prick +0 -316
  63. data/lib/ext/algorithm.rb +0 -14
  64. data/lib/ext/fileutils.rb +0 -26
  65. data/lib/ext/forward_method.rb +0 -18
  66. data/lib/ext/pg.rb +0 -18
  67. data/lib/ext/shortest_path.rb +0 -44
  68. data/lib/prick/archive.rb +0 -124
  69. data/lib/prick/branch.rb +0 -265
  70. data/lib/prick/builder.rb +0 -246
  71. data/lib/prick/cache.rb +0 -34
  72. data/lib/prick/command.rb +0 -104
  73. data/lib/prick/database.rb +0 -82
  74. data/lib/prick/dsort.rb +0 -151
  75. data/lib/prick/ensure.rb +0 -119
  76. data/lib/prick/exceptions.rb +0 -25
  77. data/lib/prick/head.rb +0 -189
  78. data/lib/prick/migration.rb +0 -70
  79. data/lib/prick/program.rb +0 -287
  80. data/lib/prick/project.rb +0 -626
  81. data/lib/prick/rdbms.rb +0 -137
  82. data/lib/prick/schema.rb +0 -27
  83. data/lib/prick/share.rb +0 -64
  84. data/libexec/strip-comments +0 -33
  85. data/make_releases +0 -72
  86. data/make_schema +0 -10
  87. data/share/diff/diff.after-tables.sql +0 -4
  88. data/share/diff/diff.before-tables.sql +0 -4
  89. data/share/diff/diff.tables.sql +0 -8
  90. data/share/features/diff.sql +0 -2
  91. data/share/features/feature/diff.sql +0 -2
  92. data/share/features/feature/migrate.sql +0 -2
  93. data/share/features/features.sql +0 -2
  94. data/share/features/features.yml +0 -2
  95. data/share/features/migrations.sql +0 -4
  96. data/share/gitignore +0 -2
  97. data/share/migration/diff.tables.sql +0 -8
  98. data/share/migration/features.yml +0 -6
  99. data/share/migration/migrate.sql +0 -3
  100. data/share/migration/migrate.yml +0 -8
  101. data/share/migration/tables.sql +0 -3
  102. data/share/schema/build.yml +0 -14
  103. data/share/schema/schema/build.yml +0 -3
  104. data/share/schema/schema/prick/build.yml +0 -14
  105. data/share/schema/schema/prick/data.sql +0 -7
  106. data/share/schema/schema/prick/schema.sql +0 -3
  107. data/share/schema/schema/public/build.yml +0 -13
  108. data/share/schema/schema.sql +0 -3
  109. data/test_assorted +0 -192
  110. data/test_feature +0 -112
  111. data/test_refactor +0 -34
  112. data/test_single_dev +0 -83
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 75523a625d7139830b419f2624f96af3c029dc1ad89b0cb40dec289f4b94b522
4
- data.tar.gz: 450c53de9ef6b3c8627df7471076b87c559f367d7aef30e009a846bffa98390c
3
+ metadata.gz: 2abdd6978f9dce4cf9570d2ad5165fef65541ed5e69bd6150086a30b232498a5
4
+ data.tar.gz: bb1accf8f541d7197c73b14407401d91d5e3b9475b205bad46bea8851d2825c6
5
5
  SHA512:
6
- metadata.gz: 98b24c53c2ae54007c46cb4157963ba049327e6ea9b73c5b6b8502367e58d9d018acdb74c9896394bd90a0335359719c0e02523f1e2965de1ed31c023285eeb9
7
- data.tar.gz: 25b7425c7f8547bdd0be1ee0a570b1b6115f08ff2ba3bd8c8284cced0ffc5da1ecbc0670e57bb21d2b9a45276df03d0f2f3210b83712ff50f55f811991ba11e3
6
+ metadata.gz: 951dee9f87d7ba9475bf70512b35d29720a30ec5371e2c03c00575c21a94163f3aa43bf28bfbb6a5fe80e0da63cb1154bd84f29ae671651b7aa54de0e45630e2
7
+ data.tar.gz: cc5ef462d6c9ae5e1add6bb9fc53ffc4a6684c64020d24fad39327cd80218f01c031060f7c2e14a125a94caad305c658994e2ea53845f211909a4764a72f8d75
data/Gemfile CHANGED
@@ -1,9 +1,15 @@
1
- source "https://rubygems.org"
1
+ # frozen_string_literal: true
2
2
 
3
- git_source(:github) {|repo_name| "https://github.com/clrgit/#{repo_name}" }
3
+ source "https://rubygems.org"
4
4
 
5
- # Specify your gem's dependencies in prick.gemspec
5
+ # Specify your gem's dependencies in prick_build.gemspec
6
6
  gemspec
7
7
 
8
- gem "rake", "~> 12.0"
8
+ gem "rake", "~> 13.0"
9
9
  gem "rspec", "~> 3.0"
10
+
11
+ gem 'shellopts', :github => 'clrgit/shellopts', branch: "master"
12
+ gem 'pg_graph', :github => 'clrgit/pg_graph', branch: "master"
13
+ gem 'pg_meta', :github => 'clrgit/pg_meta', branch: "master"
14
+ gem 'pg_conn', :github => 'clrgit/pg_conn', branch: "master"
15
+
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # Prick
1
+ # PrickBuild
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/prick`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/prick_build`. To experiment with that code, run `bin/console` for an interactive prompt.
4
4
 
5
5
  TODO: Delete this and the text above, and describe your gem
6
6
 
@@ -9,16 +9,16 @@ TODO: Delete this and the text above, and describe your gem
9
9
  Add this line to your application's Gemfile:
10
10
 
11
11
  ```ruby
12
- gem 'prick'
12
+ gem 'prick_build'
13
13
  ```
14
14
 
15
15
  And then execute:
16
16
 
17
- $ bundle
17
+ $ bundle install
18
18
 
19
19
  Or install it yourself as:
20
20
 
21
- $ gem install prick
21
+ $ gem install prick_build
22
22
 
23
23
  ## Usage
24
24
 
@@ -28,8 +28,8 @@ TODO: Write usage instructions here
28
28
 
29
29
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
30
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
32
 
33
33
  ## Contributing
34
34
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/prick.
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/prick_build.
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/gem_tasks"
2
4
  require "rspec/core/rake_task"
3
5
 
4
6
  RSpec::Core::RakeTask.new(:spec)
5
7
 
6
- task :default => :spec
8
+ task default: :spec
data/TODO CHANGED
@@ -1,13 +1,15 @@
1
1
 
2
- o Add a top-level 'global' directory for stuff that doesn't belong to a schema or that
3
- require superuser privileges. prick-build should then execute the content of that
4
- directory first before switching to the database user
5
- o Check for commits to tags
6
- o Accumulate version history in prick.versions instead of just having the newest
7
- o Using rc's in migration syntax: fork-version-fork-version.rc1 ?
8
- o Make it possible to execute prick in any subdirecty instead of only in the root
9
- o Hack migra to create separate file(s) for table changes
10
- o Also execute subject/ directory if only a subject.sql is found. subject.yml
11
- should still override everything
12
- o Track included files - this will make dependency checks much easier
2
+ o Store fox.state, reflections, and reflections in the database so third-party
3
+ programs can use them when testing
4
+ o 'prick make <resource>' should build everything up the resource, then commit
5
+ before trying to build the resource upon the database. Then it is easy to
6
+ re-run the failed (sql-)script in psql to see why it failed
7
+ o 'prick make' should build everything up a touched file, then commit before
8
+ proceeding
9
+ o Build functions after schema so that there are no order dependencies on
10
+ functions. Problem is to detect function files
11
+ o Build views after schema so that there are no order dependencies on
12
+ views. Problem is to detect view files
13
13
 
14
+ + prick release major|minor|patch
15
+ + Check modification time of build.yml files too
data/bin/console CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require "bundler/setup"
4
- require "prick"
5
+ require "prick_build"
5
6
 
6
7
  # You can add fixtures and/or initialization code here to make experimenting
7
8
  # with your gem easier. You can also use a different console, if you like.
data/doc/build-yml.txt ADDED
@@ -0,0 +1,14 @@
1
+
2
+ Format
3
+
4
+ ---
5
+ - schema: schema-name
6
+ - file
7
+ - seed: file
8
+ - seed:
9
+ - file
10
+ - file
11
+ - schema_init: file
12
+ - schema_term: file
13
+ - init: file
14
+ - term: file
data/exe/prick CHANGED
@@ -1,39 +1,250 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'prick.rb'
4
- require 'prick/program.rb'
3
+ #$LOAD_PATH.unshift "/home/clr/prj/shellopts/lib"
5
4
 
6
- require 'shellopts'
5
+ require 'bootsnap'
6
+ begin
7
+ cache_dir = "/run/user/" + `id -u`.chomp
8
+ Bootsnap.setup(
9
+ cache_dir: cache_dir, # Path to your cache
10
+ development_mode: true, # Current working environment, e.g. RACK_ENV, RAILS_ENV, etc
11
+ load_path_cache: true, # Optimize the LOAD_PATH with a cache
12
+ compile_cache_iseq: true, # Compile Ruby code into ISeq cache, breaks coverage reporting.
13
+ compile_cache_yaml: true # Compile YAML into a cache
14
+ )
15
+ Bootsnap.instrumentation = ->(event, path) { puts "#{event} #{path}" }
16
+ end
7
17
 
8
- require 'tempfile'
18
+ require 'prick.rb'
19
+ require 'shellopts'
9
20
 
10
21
  include ShellOpts
11
22
  include Prick
12
23
 
24
+ TIME = false
13
25
 
14
26
  SPEC = %(
15
- -n,name=NAME
16
- Name of project. Defauls to the environment variable `PRICK_PROJECT` if
17
- set and else the name of the current directory
18
-
19
- -C,directory=DIR
20
- Change to directory DIR before doing anything else
21
-
22
- -h,help COMMAND...
23
- Print this page
27
+ @ Prick project command
24
28
 
25
29
  +v,verbose
26
- Be verbose. Repeated --verbose options increase the verbosity level
30
+ Be verbose. Repeated -v options increase the verbosity level
27
31
 
28
32
  -q,quiet
29
33
  Be quiet
30
34
 
31
- --version
32
- Print prick version
35
+ -C,directory=EDIR
36
+ Change to directory DIR before doing anything else
37
+
38
+ -d,database=DATABASE
39
+ Override database name from prick.yml
40
+
41
+ -U,username=USERNAME
42
+ Override username from from prick.yml
43
+
44
+ version!
45
+ Print project version
46
+
47
+ init! -n,name=NAME -t,title=TITLE [DIRECTORY]
48
+ Initializes a prick project
49
+
50
+ setup!
51
+ Create the database user (if necessary) and an empty database
52
+
53
+ teardown!
54
+ Drop the database and the database user. TODO: Also run teardown scripts
55
+
56
+ create.data!
57
+ create.schema!
58
+ create.database!
59
+ create.users!
60
+ create.all!
61
+ Create an object. Fails if migration exist unless the --force flag is given
62
+
63
+ create.migration! -f,force -o,file=FILE -- VERSION
64
+ Create a migration from VERSION to the current and write it to
65
+ migration/VERSION. Fails if migration exist unless the --force flag is
66
+ given. If --file is given, the migration is written to FILE instead of
67
+ the migration directory. This doesn't require you to be on a release
68
+ branch and can be used to create ad-hoc migration scripts
69
+
70
+ drop! -- [KIND]
71
+ @ Drop objects
72
+
73
+ Kind can be 'users', 'data', 'schema', 'database' (the default), or 'all'. It is
74
+ not an error if the object doesn't exist. TODO Only 'users' is currently defined
75
+
76
+ build! -t,time --dump=KIND? -- [SCHEMA]
77
+ Build the project. If SCHEMA is defined, later schemas are excluded.
78
+ KIND can be 'nodes', 'allnodes' or 'batches' (the default)
79
+
80
+ make! -t,time --dump=KIND? -- [SCHEMA]
81
+ @ Only rebuild changed files
82
+
83
+ Checks file timestamps against the time of the last build and only
84
+ rebuild affected parts of the project. KIND can be 'nodes', 'allnodes' or
85
+ 'batches'
86
+
87
+ fox! -- FILE...
88
+ Load fox file data. Data are reset to their initial state after build
89
+ before the fox data are loaded
90
+
91
+ release! -- KIND
92
+ Create a release of the given kind. KIND can be 'major', 'minor', or
93
+ 'patch'. Release checks that the current repo is clean and up to date
94
+ with the origin
95
+
96
+ migrate! -f,file=FILE
97
+ Execute a migration
98
+
99
+ dump.type!
100
+ dump.data!
101
+ dump.schema!
102
+ dump.database!
103
+ TODO
104
+
105
+ dump.migration! --force -- VERSION
106
+ )
107
+
108
+ opts, args = ShellOpts.process(SPEC, ARGV, exception: true)
109
+
110
+ begin
111
+ # Handle verbose and quiet
112
+ $verbose = opts.verbose
113
+ $quiet = opts.quiet?
114
+
115
+ # Honor -C option
116
+ if opts.directory?
117
+ if File.exist?(opts.directory)
118
+ begin
119
+ Dir.chdir(opts.directory)
120
+ rescue Errno::ENOENT
121
+ raise Prick::Error, "Can't cd to '#{opts.directory}'"
122
+ end
123
+ else
124
+ raise Prick::Error, "Can't find directory: #{opts.directory}"
125
+ end
126
+ end
127
+
128
+ # Get subcommand
129
+ cmd = opts.subcommand!
130
+
131
+ # Process init command
132
+ if opts.subcommand == :init
133
+ dir, state = Prick::SubCommand.init(args.expect(0..1), cmd.name, cmd.title, opts.database, opts.username)
134
+ puts "Initialized prick project '#{state.name}' in #{dir}"
135
+ if opts.database.nil? || opts.username.nil?
136
+ puts
137
+ puts "Please check database/username in #{PRICK_CONTEXT_FILE}"
138
+ end
139
+ exit
140
+ end
141
+
142
+ # Check state
143
+ File.exist?(PRICK_PROJECT_FILE) or raise Prick::Error, "Not in a prick project directory"
144
+
145
+ # Load state
146
+ Prick.state = State.load
147
+
148
+ # Handle -d and -U options
149
+ database = opts.database || Prick.state.database
150
+ username = opts.username || Prick.state.username
151
+
152
+ # Expect a sub-command
153
+ cmd = opts.subcommand! or raise Prick::Error, "Subcomand expected"
154
+
155
+ # Process subcommands
156
+ case opts.subcommand
157
+ when :version!
158
+ puts "#{Prick.state.name}-#{Prick.state.version}"
159
+
160
+ when :setup!
161
+ Prick::SubCommand.setup(database, username)
162
+
163
+ when :teardown!
164
+ Prick::SubCommand.teardown(database, username)
165
+
166
+ when :create!
167
+ create_command = opts.create!
168
+ case create_command.subcommand
169
+ when :migration
170
+ arg = args.expect(1)
171
+ version = PrickVersion.try(arg) or raise Prick::Error, "Illegal version: #{arg}"
172
+ Prick::SubCommand.create_migration(
173
+ username, version,
174
+ force: create_command.subcommand!.force?,
175
+ file: create_command.subcommand!.file)
176
+ else
177
+ raise NotImplementedError
178
+ end
179
+
180
+ when :build!
181
+ dump = cmd.dump("batches")&.to_sym
182
+ Prick::SubCommand.build(database, username, args.expect(0..1), timer: cmd.time?, dump: dump)
183
+
184
+ when :make!
185
+ dump = cmd.dump("batches")&.to_sym
186
+ Prick::SubCommand.make(database, username, args.expect(0..1), timer: cmd.time?, dump: dump)
187
+
188
+ when :fox!
189
+ Prick::SubCommand.fox(database, username, args)
33
190
 
34
- init! -u,user=USER [DIR]
35
- Initialize a project in the given directory. DIR defaults to the current
36
- directory. The USER is the postgres user, it defaults to the project name
191
+ when :drop!
192
+ case subject = args.expect(1).to_sym
193
+ when :all
194
+ Prick::SubCommand.drop_all(database)
195
+ when :users
196
+ Prick::SubCommand.drop_users(database)
197
+ when :database
198
+ Prick::SubCommand.drop_database(database)
199
+ when :data, :schema, :database, :all
200
+ raise NotImplementedError
201
+ else
202
+ raise Prick::Error, "Unknown subject: #{subject}"
203
+ end
204
+
205
+ when :release!
206
+ kind = args.expect(1).to_sym
207
+ constrain? kind, :major, :minor, :patch or
208
+ raise Prick::Fail, "Expected 'major', 'minor', or 'patch' argument, got '#{kind}'"
209
+ Prick::SubCommand.release(kind)
210
+
211
+ when :migrate!
212
+ args.expect(0)
213
+ Prick::SubCommand.migrate(database, username, file: cmd.file)
214
+
215
+ when :dump!
216
+ subject = cmd.subcommand!
217
+ case cmd.subcommand
218
+ when :migration
219
+ arg = args.expect(1)
220
+ version = PrickVersion.try(arg) or raise "Illegal version number: #{arg}"
221
+ Prick::SubCommand.create_migration(username, version, force: subject.force?, file: "/dev/stdout")
222
+ when :data, :schema, :database
223
+ raise NotImplementedError
224
+ else
225
+ raise Prick::Error, "Unknown subject: #{subject}"
226
+ end
227
+
228
+ else
229
+ raise Prick::Fail, "Internal error: Unhandled command - #{opts.subcommand.inspect}"
230
+ end
231
+
232
+ rescue ShellOpts::Failure, Prick::Fail, Prick::Build::PostgresError => ex
233
+ ShellOpts.failure(ex.message)
234
+
235
+ rescue ShellOpts::Error, Prick::Error => ex
236
+ ShellOpts.error(ex.message)
237
+ end
238
+
239
+ __END__
240
+
241
+ -n,name=NAME
242
+ Name of project. Defauls to the environment variable `PRICK_PROJECT` if
243
+ set and else the name of the current directory
244
+
245
+ init! -u,user=USER [NAME]
246
+ Initialize a project in the given directory. The USER is the postgres
247
+ user and defaults to the project name
37
248
 
38
249
  info!
39
250
  Print project information
@@ -160,6 +371,13 @@ SPEC = %(
160
371
  in the var/spool directory
161
372
  )
162
373
 
374
+ __END__
375
+
376
+
377
+
378
+
379
+
380
+
163
381
  DEFAULT_STATE_FILE = "fox.state"
164
382
 
165
383
  opts, args = ShellOpts.process(SPEC, ARGV)
@@ -0,0 +1,147 @@
1
+ module Prick
2
+ module Build
3
+ class BuildBatch
4
+ include Timer
5
+
6
+ attr_reader :builder
7
+ forward_to :builder, :conn
8
+
9
+ def kind() @nodes.first&.kind end
10
+ attr_reader :nodes
11
+
12
+ def initialize(builder)
13
+ @builder = builder
14
+ @nodes = []
15
+ end
16
+
17
+ def execute(&block)
18
+ name = self.class.to_s.sub(/.*::/, "").split(/(?=[A-Z])/).map(&:downcase).join(" ")
19
+ time "Execute #{name} (nodes: #{nodes.size})" do
20
+ yield
21
+ end
22
+ end
23
+
24
+ def dump
25
+ puts kind.upcase
26
+ indent { nodes.each(&:dump) }
27
+ end
28
+ end
29
+
30
+ class SqlBatch < BuildBatch
31
+ def execute
32
+ super {
33
+ begin
34
+ sql = []
35
+ if nodes.first.is_a?(ExeNode)
36
+ time "Execute script" do
37
+ sql = [nodes.first.source]
38
+ end
39
+ end
40
+ conn.execute sql + nodes[sql.size..-1].map(&:source)
41
+
42
+ rescue PG::SyntaxError, PG::Error => ex
43
+ error, line, pos = parse_pg_message(ex.message)
44
+ if line
45
+ if line < nodes.first.lines
46
+ node = nodes.first;
47
+ else
48
+ line -= nodes.first.lines
49
+ node = nodes[1..-1].find { |node|
50
+ line -= 1
51
+ if line < node.lines
52
+ line -= 2
53
+ true
54
+ else
55
+ line -= node.lines
56
+ false
57
+ end
58
+ }
59
+ end
60
+
61
+ if node.is_a?(SqlNode)
62
+ message = ["prick: #{error} in #{node.path}", line, pos].compact.join(":")
63
+ else
64
+ message = "prick: #{error} from #{node.path}"
65
+ end
66
+ else
67
+ raise
68
+ end
69
+ raise PostgresError.new(message)
70
+ end
71
+ }
72
+ end
73
+
74
+ private
75
+ # Returns [error, line, pos] tuple
76
+ def parse_pg_message(message)
77
+ message =~ /ERROR:\s*(.*?)\n(LINE (\d+): ).*?\n(\s*\^)/m or return nil
78
+ [$1, $3.to_i, $4.size - $2.size]
79
+ end
80
+ end
81
+
82
+ class ModuleBatch < BuildBatch
83
+ def execute
84
+ super {
85
+ nodes.each { |node|
86
+ sql = node&.call
87
+ conn.execute sql if sql
88
+ }
89
+ }
90
+ end
91
+ end
92
+
93
+ class FoxBatch < BuildBatch
94
+ # Inlining Fox instead of using command line program as it saves some deciseconds
95
+ def execute
96
+ t_type = t_fox = t_exe = nil
97
+
98
+ super {
99
+ timer = Timer.new "Execute fox batch (nodes: #{nodes.size}, clean: #{builder.clean})"
100
+ t_type = Timer.new " Load data model"
101
+
102
+ files = nodes.map(&:path)
103
+
104
+ # Load meta object
105
+ meta = PgMeta.new(conn)
106
+
107
+ # Create type object
108
+ type = PgGraph::Type.new(meta, builder.reflections_file)
109
+
110
+ t_type.stop
111
+ t_fox = Timer.new " Parse files"
112
+
113
+ # Create fox object
114
+ fox = FixtureFox::Fox.new(type)
115
+
116
+ # Parse files
117
+ for file in files
118
+ source, lines = fox.tokenize(file)
119
+ fox.parse(source, lines)
120
+ end
121
+
122
+ # Analyze
123
+ fox.assign_types
124
+ fox.generate
125
+
126
+ # Dump state file
127
+ fox.write_state(FOX_STATE_PATH)
128
+
129
+ t_fox.stop
130
+ t_exe = Timer.new " Execute SQL"
131
+
132
+ delete = builder.clean ? :none : :touched
133
+ begin
134
+ conn.execute fox.to_sql(format: :exec, delete: delete)
135
+ conn.execute "update prick.versions set built_at = now() at time zone 'UTC'"
136
+ rescue PG::SyntaxError, PG::Error => ex
137
+ raise PostgresError, "prick: #{error} from #{node.path}"
138
+ end
139
+ }
140
+ t_type.emit
141
+ t_fox.emit
142
+ t_exe.emit
143
+ end
144
+ end
145
+ end
146
+ end
147
+
@@ -0,0 +1,122 @@
1
+ require 'local/command'
2
+
3
+ require 'builder/node.rb'
4
+ require 'builder/node_pool.rb'
5
+ require 'builder/batch.rb'
6
+ require 'builder/parser.rb'
7
+
8
+ include Constrain
9
+
10
+ module Prick
11
+ module Build
12
+ class Error < StandardError; end
13
+ class PostgresError < Error; end
14
+
15
+ class Builder
16
+ # PgConn object
17
+ attr_reader :conn
18
+
19
+ # Root schema directory
20
+ attr_reader :dir
21
+
22
+ # Reflections YAML file
23
+ attr_reader :reflections_file
24
+
25
+ # True if database is initially clean - ie. all tables are empty
26
+ attr_accessor :clean
27
+
28
+ # Root build node
29
+ attr_reader :root
30
+
31
+ # Pool of nodes. Initialized by #load_pool
32
+ attr_reader :pool
33
+
34
+ forward_to :pool, :nodes, :decl_nodes, :init_nodes, :term_nodes,
35
+ :seed_nodes, :fox_seed_nodes, :sql_seed_nodes,
36
+ :schemas
37
+
38
+ def batches() @batches ||= group end
39
+
40
+ def initialize(conn, dir, clean = true, touched: false)
41
+ @conn = conn
42
+ @dir = dir
43
+ @reflections_file = REFLECTIONS_PATH
44
+ @clean = clean
45
+ @pool = NodePool.new
46
+ @root = Parser.parse(conn, dir)
47
+ load_pool(@root) # Collect nodes into pool
48
+ @batches = nil # Initialized by #group
49
+ end
50
+
51
+ # Group sources into batches
52
+ def group
53
+ @batches = []
54
+ kind = nil
55
+ batch = nil
56
+
57
+ for node in [init_nodes, decl_nodes, fox_seed_nodes, sql_seed_nodes, term_nodes].flatten
58
+ case node.kind
59
+ when :module
60
+ if batch&.kind != :module
61
+ @batches << batch if batch
62
+ batch = ModuleBatch.new(self)
63
+ end
64
+ when :exe # Exe sources always create a new batch
65
+ @batches << batch if batch
66
+ batch = SqlBatch.new(self)
67
+ when batch&.kind
68
+ ;
69
+ when :sql || node.kind == :inline
70
+ if batch&.kind != :exe
71
+ @batches << batch if batch
72
+ batch = SqlBatch.new(self)
73
+ end
74
+ when :inline
75
+ @batches << batch if batch
76
+ batch = SqlBatch.new(self)
77
+ when :fox
78
+ @batches << batch if batch
79
+ batch = FoxBatch.new(self)
80
+ when :yml
81
+ next
82
+ else
83
+ raise Error, "Unexpected node kind: #{node.kind}"
84
+ end
85
+ batch.nodes << node
86
+ end
87
+ @batches << batch if batch
88
+ end
89
+
90
+ def execute(conn, create_schemas: schemas)
91
+ group if batches.nil?
92
+ conn.exec create_schemas.grep_v("public").map { |schema| "create schema #{schema}" }
93
+ for batch in batches
94
+ batch.execute
95
+ end
96
+ end
97
+
98
+ # def setup
99
+ # end
100
+
101
+ # def teardown
102
+ # end
103
+
104
+ def dump
105
+ batches ? batches.each(&:dump) : pool.dump
106
+ end
107
+
108
+ private
109
+ def load_pool(build_node)
110
+ pool.add(build_node.init_nodes)
111
+ build_node.decl_nodes.each { |node|
112
+ pool.add node
113
+ load_pool(node) if node.kind == :yml
114
+ }
115
+ pool.add(build_node.seed_nodes)
116
+ pool.add(build_node.term_nodes)
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+