prick 0.19.0 → 0.20.1

Sign up to get free protection for your applications and to get access to all the features.
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
+