@@ -74,7 +74,7 @@ defmodule AshPostgres.MigrationGenerator do
7474|> group_into_phases ( )
7575|> comment_out_phases ( )
7676|> build_up_and_down ( )
77- |> write_migration ( snapshots , repo , opts , tenant? )
77+ |> write_migration! ( snapshots , repo , opts , tenant? )
7878end
7979end )
8080end
@@ -302,47 +302,9 @@ defmodule AshPostgres.MigrationGenerator do
302302|> Enum . sort ( )
303303end
304304
305- defp write_migration ( { up , down } , snapshots , repo , opts , tenant? ) do
305+ defp write_migration! ( { up , down } , snapshots , repo , opts , tenant? ) do
306306repo_name = repo |> Module . split ( ) |> List . last ( ) |> Macro . underscore ( )
307307
308- unless opts . dry_run do
309- Enum . each ( snapshots , fn snapshot ->
310- snapshot_binary = snapshot_to_binary ( snapshot )
311-
312- snapshot_table = "#{ timestamp ( ) } _#{ snapshot . table } "
313-
314- snapshot_folder =
315- if tenant? do
316- opts . snapshot_path
317- |> Path . join ( repo_name )
318- |> Path . join ( "tenants" )
319- else
320- opts . snapshot_path
321- |> Path . join ( repo_name )
322- end
323-
324- snapshot_file =
325- snapshot_folder
326- |> Path . join ( snapshot_table <> ".json" )
327-
328- File . mkdir_p ( Path . dirname ( snapshot_file ) )
329- File . write! ( snapshot_file , snapshot_binary , [ ] )
330-
331- # create a new version file to track latest migration file
332- version_file =
333- snapshot_folder
334- |> Path . join ( snapshot . table <> ".version.json" )
335-
336- version_binary =
337- snapshot_to_binary ( % {
338- latest_version: snapshot_file
339- } )
340-
341- File . mkdir_p ( Path . dirname ( version_file ) )
342- File . write! ( version_file , version_binary , [ ] )
343- end )
344- end
345-
346308migration_path =
347309if tenant? do
348310if opts . tenant_migration_path do
@@ -438,10 +400,73 @@ defmodule AshPostgres.MigrationGenerator do
438400 end
439401 """
440402
441- if opts . dry_run do
442- Mix . shell ( ) . info ( format ( contents , opts ) )
443- else
444- create_file ( migration_file , format ( contents , opts ) )
403+ try do
404+ contents = format ( contents , opts )
405+
406+ create_new_snapshot ( snapshots , repo_name , opts , tenant? )
407+
408+ if opts . dry_run do
409+ Mix . shell ( ) . info ( contents )
410+ else
411+ create_file ( migration_file , contents )
412+ end
413+ rescue
414+ exception ->
415+ reraise (
416+ """
417+ Exception while formatting generated code:
418+ #{ Exception . format ( :error , exception , __STACKTRACE__ ) }
419+
420+ Code:
421+
422+ #{ add_line_numbers ( contents ) }
423+
424+ To generate it unformatted anyway, but manually fix it, use the `--no-format` option.
425+ """ ,
426+ __STACKTRACE__
427+ )
428+ end
429+ end
430+
431+ defp add_line_numbers ( contents ) do
432+ lines = String . split ( contents , "\n " )
433+
434+ digits = String . length ( to_string ( Enum . count ( lines ) ) )
435+
436+ lines
437+ |> Enum . with_index ( )
438+ |> Enum . map_join ( "\n " , fn { line , index } ->
439+ "#{ String . pad_trailing ( to_string ( index ) , digits , " " ) } |#{ line } "
440+ end )
441+ end
442+
443+ defp create_new_snapshot ( snapshots , repo_name , opts , tenant? ) do
444+ unless opts . dry_run do
445+ Enum . each ( snapshots , fn snapshot ->
446+ snapshot_binary = snapshot_to_binary ( snapshot )
447+
448+ snapshot_folder =
449+ if tenant? do
450+ opts . snapshot_path
451+ |> Path . join ( repo_name )
452+ |> Path . join ( "tenants" )
453+ else
454+ opts . snapshot_path
455+ |> Path . join ( repo_name )
456+ end
457+
458+ snapshot_file = Path . join ( snapshot_folder , "#{ snapshot . table } /#{ timestamp ( ) } .json" )
459+
460+ File . mkdir_p ( Path . dirname ( snapshot_file ) )
461+ File . write! ( snapshot_file , snapshot_binary , [ ] )
462+
463+ old_snapshot_folder = Path . join ( snapshot_folder , "#{ snapshot . table } .json" )
464+
465+ if File . exists? ( old_snapshot_folder ) do
466+ new_snapshot_folder = Path . join ( snapshot_folder , "#{ snapshot . table } /initial.json" )
467+ File . rename ( old_snapshot_folder , new_snapshot_folder )
468+ end
469+ end )
445470end
446471end
447472
@@ -951,27 +976,42 @@ defmodule AshPostgres.MigrationGenerator do
951976|> Path . join ( repo_name )
952977|> Path . join ( "tenants" )
953978else
954- Path . join ( opts . snapshot_path , repo_name )
979+ opts . snapshot_path
980+ |> Path . join ( repo_name )
955981end
956982
957- # get name of latest version file.
958- version_file = Path . join ( folder , snapshot . table <> ".version.json" )
983+ snapshot_folder = Path . join ( folder , snapshot . table )
959984
960- file =
961- if File . exists? ( version_file ) do
962- file_content =
963- version_file
964- |> File . read! ( )
965- |> Jason . decode! ( keys: :atoms! )
985+ if File . exists? ( snapshot_folder ) do
986+ snapshot_folder
987+ |> File . ls! ( )
988+ |> Enum . filter ( & String . ends_with? ( & 1 , ".json" ) )
989+ |> Enum . map ( & String . trim_trailing ( & 1 , ".json" ) )
990+ |> Enum . map ( & Integer . parse / 1 )
991+ |> Enum . filter ( fn { _int , remaining } -> remaining == "" end )
992+ |> Enum . map ( & elem ( & 1 , 0 ) )
993+ |> case do
994+ [ ] ->
995+ get_old_snapshot ( folder , snapshot )
966996
967- file_content . latest_version
968- else
969- version_file = Path . join ( folder , snapshot . table <> ".json" )
970- version_file
997+ timestamps ->
998+ timestamp = Enum . max ( timestamps )
999+ snapshot_file = Path . join ( snapshot_folder , "#{ timestamp } .json" )
1000+
1001+ snapshot_file
1002+ |> File . read! ( )
1003+ |> load_snapshot ( )
9711004end
1005+ else
1006+ get_old_snapshot ( folder , snapshot )
1007+ end
1008+ end
9721009
973- if File . exists? ( file ) do
974- file
1010+ defp get_old_snapshot ( folder , snapshot ) do
1011+ old_snapshot_file = Path . join ( folder , "#{ snapshot . table } .json" )
1012+ # This is adapter code for the old version, where migrations were stored in a flat directory
1013+ if File . exists? ( old_snapshot_file ) do
1014+ old_snapshot_file
9751015|> File . read! ( )
9761016|> load_snapshot ( )
9771017end
@@ -1206,6 +1246,8 @@ defmodule AshPostgres.MigrationGenerator do
12061246attribute
12071247|> Map . update! ( :type , & String . to_atom / 1 )
12081248|> Map . update! ( :name , & String . to_atom / 1 )
1249+ |> Map . put_new ( :default , "nil" )
1250+ |> Map . update! ( :default , & ( & 1 || "nil" ) )
12091251|> Map . update! ( :references , fn
12101252nil ->
12111253nil