hanami-model 1.2.0 → 1.3.3

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +10 -7
  5. data/hanami-model.gemspec +25 -20
  6. data/lib/hanami-model.rb +3 -1
  7. data/lib/hanami/entity.rb +6 -3
  8. data/lib/hanami/entity/schema.rb +10 -7
  9. data/lib/hanami/model.rb +15 -12
  10. data/lib/hanami/model/association.rb +7 -7
  11. data/lib/hanami/model/associations/belongs_to.rb +3 -1
  12. data/lib/hanami/model/associations/dsl.rb +2 -2
  13. data/lib/hanami/model/associations/has_many.rb +10 -8
  14. data/lib/hanami/model/associations/has_one.rb +9 -7
  15. data/lib/hanami/model/associations/many_to_many.rb +9 -11
  16. data/lib/hanami/model/configuration.rb +29 -10
  17. data/lib/hanami/model/configurator.rb +5 -3
  18. data/lib/hanami/model/entity_name.rb +4 -2
  19. data/lib/hanami/model/error.rb +18 -7
  20. data/lib/hanami/model/mapped_relation.rb +4 -2
  21. data/lib/hanami/model/mapping.rb +3 -1
  22. data/lib/hanami/model/migration.rb +2 -0
  23. data/lib/hanami/model/migrator.rb +7 -5
  24. data/lib/hanami/model/migrator/adapter.rb +14 -12
  25. data/lib/hanami/model/migrator/connection.rb +16 -9
  26. data/lib/hanami/model/migrator/logger.rb +3 -1
  27. data/lib/hanami/model/migrator/mysql_adapter.rb +23 -13
  28. data/lib/hanami/model/migrator/postgres_adapter.rb +31 -31
  29. data/lib/hanami/model/migrator/sqlite_adapter.rb +7 -9
  30. data/lib/hanami/model/plugins.rb +5 -3
  31. data/lib/hanami/model/plugins/mapping.rb +2 -0
  32. data/lib/hanami/model/plugins/schema.rb +2 -0
  33. data/lib/hanami/model/plugins/timestamps.rb +3 -0
  34. data/lib/hanami/model/relation_name.rb +4 -2
  35. data/lib/hanami/model/sql.rb +9 -7
  36. data/lib/hanami/model/sql/console.rb +10 -8
  37. data/lib/hanami/model/sql/consoles/abstract.rb +3 -1
  38. data/lib/hanami/model/sql/consoles/mysql.rb +4 -2
  39. data/lib/hanami/model/sql/consoles/postgresql.rb +10 -8
  40. data/lib/hanami/model/sql/consoles/sqlite.rb +6 -4
  41. data/lib/hanami/model/sql/entity/schema.rb +6 -4
  42. data/lib/hanami/model/sql/types.rb +27 -27
  43. data/lib/hanami/model/sql/types/schema/coercions.rb +5 -4
  44. data/lib/hanami/model/types.rb +4 -4
  45. data/lib/hanami/model/version.rb +3 -1
  46. data/lib/hanami/repository.rb +20 -31
  47. metadata +64 -8
@@ -1,4 +1,6 @@
1
- require 'hanami/logger'
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/logger"
2
4
 
3
5
  module Hanami
4
6
  module Model
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Hanami
2
4
  module Model
3
5
  class Migrator
@@ -8,23 +10,27 @@ module Hanami
8
10
  class MySQLAdapter < Adapter
9
11
  # @since 0.7.0
10
12
  # @api private
11
- PASSWORD = 'MYSQL_PWD'.freeze
13
+ PASSWORD = "MYSQL_PWD"
14
+
15
+ # @since 1.3.3
16
+ # @api private
17
+ DEFAULT_PORT = 3306
12
18
 
13
19
  # @since 1.0.0
14
20
  # @api private
15
- DB_CREATION_ERROR = 'Database creation failed. If the database exists, ' \
16
- 'then its console may be open. See this issue for more details: ' \
17
- 'https://github.com/hanami/model/issues/250'.freeze
21
+ DB_CREATION_ERROR = "Database creation failed. If the database exists, " \
22
+ "then its console may be open. See this issue for more details: " \
23
+ "https://github.com/hanami/model/issues/250"
18
24
 
19
25
  # @since 0.4.0
20
26
  # @api private
21
27
  def create
22
28
  new_connection(global: true).run %(CREATE DATABASE `#{database}`;)
23
- rescue Sequel::DatabaseError => e
24
- message = if e.message.match(/database exists/) # rubocop:disable Performance/RedundantMatch
29
+ rescue Sequel::DatabaseError => exception
30
+ message = if exception.message.match(/database exists/)
25
31
  DB_CREATION_ERROR
26
32
  else
27
- e.message
33
+ exception.message
28
34
  end
29
35
 
30
36
  raise MigrationError.new(message)
@@ -34,11 +40,11 @@ module Hanami
34
40
  # @api private
35
41
  def drop
36
42
  new_connection(global: true).run %(DROP DATABASE `#{database}`;)
37
- rescue Sequel::DatabaseError => e
38
- message = if e.message.match(/doesn\'t exist/) # rubocop:disable Performance/RedundantMatch
43
+ rescue Sequel::DatabaseError => exception
44
+ message = if exception.message.match(/doesn\'t exist/)
39
45
  "Cannot find database: #{database}"
40
46
  else
41
- e.message
47
+ exception.message
42
48
  end
43
49
 
44
50
  raise MigrationError.new(message)
@@ -65,22 +71,26 @@ module Hanami
65
71
  connection.password
66
72
  end
67
73
 
74
+ def port
75
+ super || DEFAULT_PORT
76
+ end
77
+
68
78
  # @since 0.4.0
69
79
  # @api private
70
80
  def dump_structure
71
- execute "mysqldump --host=#{host} --port=#{port} --user=#{username} --no-data --skip-comments --ignore-table=#{database}.#{migrations_table} #{database} > #{schema}", env: { PASSWORD => password }
81
+ execute "mysqldump --host=#{host} --port=#{port} --user=#{username} --no-data --skip-comments --ignore-table=#{database}.#{migrations_table} #{database} > #{schema}", env: {PASSWORD => password}
72
82
  end
73
83
 
74
84
  # @since 0.4.0
75
85
  # @api private
76
86
  def load_structure
77
- execute("mysql --host=#{host} --port=#{port} --user=#{username} #{database} < #{escape(schema)}", env: { PASSWORD => password }) if schema.exist?
87
+ execute("mysql --host=#{host} --port=#{port} --user=#{username} #{database} < #{escape(schema)}", env: {PASSWORD => password}) if schema.exist?
78
88
  end
79
89
 
80
90
  # @since 0.4.0
81
91
  # @api private
82
92
  def dump_migrations_data
83
- execute "mysqldump --host=#{host} --port=#{port} --user=#{username} --skip-comments #{database} #{migrations_table} >> #{schema}", env: { PASSWORD => password }
93
+ execute "mysqldump --host=#{host} --port=#{port} --user=#{username} --skip-comments #{database} #{migrations_table} >> #{schema}", env: {PASSWORD => password}
84
94
  end
85
95
  end
86
96
  end
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/utils/blank"
4
+
1
5
  module Hanami
2
6
  module Model
3
7
  class Migrator
@@ -8,46 +12,41 @@ module Hanami
8
12
  class PostgresAdapter < Adapter
9
13
  # @since 0.4.0
10
14
  # @api private
11
- HOST = 'PGHOST'.freeze
15
+ HOST = "PGHOST"
12
16
 
13
17
  # @since 0.4.0
14
18
  # @api private
15
- PORT = 'PGPORT'.freeze
19
+ PORT = "PGPORT"
16
20
 
17
21
  # @since 0.4.0
18
22
  # @api private
19
- USER = 'PGUSER'.freeze
23
+ USER = "PGUSER"
20
24
 
21
25
  # @since 0.4.0
22
26
  # @api private
23
- PASSWORD = 'PGPASSWORD'.freeze
27
+ PASSWORD = "PGPASSWORD"
24
28
 
25
29
  # @since 1.0.0
26
30
  # @api private
27
- DB_CREATION_ERROR = 'createdb: database creation failed. If the database exists, ' \
28
- 'then its console may be open. See this issue for more details: ' \
29
- 'https://github.com/hanami/model/issues/250'.freeze
31
+ DB_CREATION_ERROR = "createdb: database creation failed. If the database exists, " \
32
+ "then its console may be open. See this issue for more details: " \
33
+ "https://github.com/hanami/model/issues/250"
30
34
 
31
35
  # @since 0.4.0
32
36
  # @api private
33
37
  def create
34
- set_environment_variables
35
-
36
- call_db_command('createdb')
38
+ call_db_command("createdb")
37
39
  end
38
40
 
39
41
  # @since 0.4.0
40
42
  # @api private
41
43
  def drop
42
- set_environment_variables
43
-
44
- call_db_command('dropdb')
44
+ call_db_command("dropdb")
45
45
  end
46
46
 
47
47
  # @since 0.4.0
48
48
  # @api private
49
49
  def dump
50
- set_environment_variables
51
50
  dump_structure
52
51
  dump_migrations_data
53
52
  end
@@ -55,53 +54,54 @@ module Hanami
55
54
  # @since 0.4.0
56
55
  # @api private
57
56
  def load
58
- set_environment_variables
59
57
  load_structure
60
58
  end
61
59
 
62
60
  private
63
61
 
64
- # @since 0.4.0
62
+ # @since 1.3.3
65
63
  # @api private
66
- def set_environment_variables
67
- ENV[HOST] = host unless host.nil?
68
- ENV[PORT] = port.to_s unless port.nil?
69
- ENV[PASSWORD] = password unless password.nil?
70
- ENV[USER] = username unless username.nil?
64
+ def environment_variables
65
+ {}.tap do |env|
66
+ env[HOST] = host unless host.nil?
67
+ env[PORT] = port.to_s unless port.nil?
68
+ env[PASSWORD] = password unless password.nil?
69
+ env[USER] = username unless username.nil?
70
+ end
71
71
  end
72
72
 
73
73
  # @since 0.4.0
74
74
  # @api private
75
75
  def dump_structure
76
- execute "pg_dump -s -x -O -T #{migrations_table} -f #{escape(schema)} #{database}"
76
+ execute "pg_dump -s -x -O -T #{migrations_table} -f #{escape(schema)} #{database}", env: environment_variables
77
77
  end
78
78
 
79
79
  # @since 0.4.0
80
80
  # @api private
81
81
  def load_structure
82
- execute "psql -X -q -f #{escape(schema)} #{database}" if schema.exist?
82
+ return unless schema.exist?
83
+
84
+ execute "psql -X -q -f #{escape(schema)} #{database}", env: environment_variables
83
85
  end
84
86
 
85
87
  # @since 0.4.0
86
88
  # @api private
87
89
  def dump_migrations_data
88
90
  error = ->(err) { raise MigrationError.new(err) unless err =~ /no matching tables/i }
89
- execute "pg_dump -t #{migrations_table} #{database} >> #{escape(schema)}", error: error
91
+ execute "pg_dump -t #{migrations_table} #{database} >> #{escape(schema)}", error: error, env: environment_variables
90
92
  end
91
93
 
92
94
  # @since 0.5.1
93
95
  # @api private
94
96
  def call_db_command(command)
95
- require 'open3'
97
+ require "open3"
96
98
 
97
99
  begin
98
- Open3.popen3(command, database) do |_stdin, _stdout, stderr, wait_thr|
99
- unless wait_thr.value.success? # wait_thr.value is the exit status
100
- raise MigrationError.new(modified_message(stderr.read))
101
- end
100
+ Open3.popen3(environment_variables, command, database) do |_stdin, _stdout, stderr, wait_thr|
101
+ raise MigrationError.new(modified_message(stderr.read)) unless wait_thr.value.success? # wait_thr.value is the exit status
102
102
  end
103
- rescue SystemCallError => e
104
- raise MigrationError.new(modified_message(e.message))
103
+ rescue SystemCallError => exception
104
+ raise MigrationError.new(modified_message(exception.message))
105
105
  end
106
106
  end
107
107
 
@@ -1,6 +1,8 @@
1
- require 'pathname'
2
- require 'hanami/utils'
3
- require 'English'
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require "hanami/utils"
5
+ require "English"
4
6
 
5
7
  module Hanami
6
8
  module Model
@@ -71,7 +73,7 @@ module Hanami
71
73
  # @api private
72
74
  def path
73
75
  root.join(
74
- @connection.uri.sub(/\A(jdbc:sqlite:\/\/|sqlite:\/\/)/, '')
76
+ @connection.uri.sub(/\A(jdbc:sqlite:\/\/|sqlite:\/\/)/, "")
75
77
  )
76
78
  end
77
79
 
@@ -104,13 +106,11 @@ module Hanami
104
106
  # @since 0.4.0
105
107
  # @api private
106
108
  #
107
- # rubocop:disable Metrics/AbcSize
108
- # rubocop:disable Metrics/MethodLength
109
109
  def dump_migrations_data
110
110
  execute "sqlite3 #{escape(path)} .dump" do |stdout|
111
111
  begin
112
112
  contents = stdout.read.split($INPUT_RECORD_SEPARATOR)
113
- contents = contents.grep(/^INSERT INTO "#{migrations_table}"/)
113
+ contents = contents.grep(/^INSERT INTO "?#{migrations_table}"?/)
114
114
 
115
115
  ::File.open(schema, ::File::CREAT | ::File::BINARY | ::File::WRONLY | ::File::APPEND) do |file|
116
116
  file.write(contents.join($INPUT_RECORD_SEPARATOR))
@@ -120,8 +120,6 @@ module Hanami
120
120
  end
121
121
  end
122
122
  end
123
- # rubocop:enable Metrics/MethodLength
124
- # rubocop:enable Metrics/AbcSize
125
123
  end
126
124
  end
127
125
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Hanami
2
4
  module Model
3
5
  # Plugins to extend read/write operations from/to the database
@@ -17,9 +19,9 @@ module Hanami
17
19
  end
18
20
  end
19
21
 
20
- require 'hanami/model/plugins/mapping'
21
- require 'hanami/model/plugins/schema'
22
- require 'hanami/model/plugins/timestamps'
22
+ require "hanami/model/plugins/mapping"
23
+ require "hanami/model/plugins/schema"
24
+ require "hanami/model/plugins/timestamps"
23
25
  end
24
26
  end
25
27
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Hanami
2
4
  module Model
3
5
  module Plugins
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Hanami
2
4
  module Model
3
5
  module Plugins
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Hanami
2
4
  module Model
3
5
  module Plugins
@@ -32,6 +34,7 @@ module Hanami
32
34
  # @api private
33
35
  def [](value)
34
36
  return @input[value] unless timestamps?
37
+
35
38
  _touch(@input[value], Time.now)
36
39
  end
37
40
 
@@ -1,5 +1,7 @@
1
- require_relative 'entity_name'
2
- require 'hanami/utils/string'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "entity_name"
4
+ require "hanami/utils/string"
3
5
 
4
6
  module Hanami
5
7
  module Model
@@ -1,12 +1,14 @@
1
- require 'rom-sql'
2
- require 'hanami/utils'
1
+ # frozen_string_literal: true
2
+
3
+ require "rom-sql"
4
+ require "hanami/utils"
3
5
 
4
6
  module Hanami
5
7
  # Hanami::Model migrations
6
8
  module Model
7
- require 'hanami/model/error'
8
- require 'hanami/model/association'
9
- require 'hanami/model/migration'
9
+ require "hanami/model/error"
10
+ require "hanami/model/association"
11
+ require "hanami/model/migration"
10
12
 
11
13
  # Define a migration
12
14
  #
@@ -53,8 +55,8 @@ module Hanami
53
55
  #
54
56
  # @since 0.7.0
55
57
  module Sql
56
- require 'hanami/model/sql/types'
57
- require 'hanami/model/sql/entity/schema'
58
+ require "hanami/model/sql/types"
59
+ require "hanami/model/sql/entity/schema"
58
60
 
59
61
  # Returns a SQL fragment that references a database function by the given name
60
62
  # This is useful for database migrations
@@ -1,4 +1,6 @@
1
- require 'uri'
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
2
4
 
3
5
  module Hanami
4
6
  module Model
@@ -24,16 +26,16 @@ module Hanami
24
26
 
25
27
  # @since 0.7.0
26
28
  # @api private
27
- def console # rubocop:disable Metrics/MethodLength
29
+ def console
28
30
  case @uri.scheme
29
- when 'sqlite'
30
- require 'hanami/model/sql/consoles/sqlite'
31
+ when "sqlite"
32
+ require "hanami/model/sql/consoles/sqlite"
31
33
  Sql::Consoles::Sqlite.new(@uri)
32
- when 'postgres', 'postgresql'
33
- require 'hanami/model/sql/consoles/postgresql'
34
+ when "postgres", "postgresql"
35
+ require "hanami/model/sql/consoles/postgresql"
34
36
  Sql::Consoles::Postgresql.new(@uri)
35
- when 'mysql', 'mysql2'
36
- require 'hanami/model/sql/consoles/mysql'
37
+ when "mysql", "mysql2"
38
+ require "hanami/model/sql/consoles/mysql"
37
39
  Sql::Consoles::Mysql.new(@uri)
38
40
  end
39
41
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Hanami
2
4
  module Model
3
5
  module Sql
@@ -18,7 +20,7 @@ module Hanami
18
20
  # @since 0.7.0
19
21
  # @api private
20
22
  def database_name
21
- @uri.path.sub(/^\//, '')
23
+ @uri.path.sub(/^\//, "")
22
24
  end
23
25
 
24
26
  # @since 0.7.0
@@ -1,4 +1,6 @@
1
- require_relative 'abstract'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "abstract"
2
4
 
3
5
  module Hanami
4
6
  module Model
@@ -11,7 +13,7 @@ module Hanami
11
13
  class Mysql < Abstract
12
14
  # @since 0.7.0
13
15
  # @api private
14
- COMMAND = 'mysql'.freeze
16
+ COMMAND = "mysql"
15
17
 
16
18
  # @since 0.7.0
17
19
  # @api private
@@ -1,5 +1,7 @@
1
- require_relative 'abstract'
2
- require 'cgi'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "abstract"
4
+ require "cgi"
3
5
 
4
6
  module Hanami
5
7
  module Model
@@ -12,11 +14,11 @@ module Hanami
12
14
  class Postgresql < Abstract
13
15
  # @since 0.7.0
14
16
  # @api private
15
- COMMAND = 'psql'.freeze
17
+ COMMAND = "psql"
16
18
 
17
19
  # @since 0.7.0
18
20
  # @api private
19
- PASSWORD = 'PGPASSWORD'.freeze
21
+ PASSWORD = "PGPASSWORD"
20
22
 
21
23
  # @since 0.7.0
22
24
  # @api private
@@ -48,22 +50,22 @@ module Hanami
48
50
  # @since 0.7.0
49
51
  # @api private
50
52
  def port
51
- port = query['port'] || @uri.port
53
+ port = query["port"] || @uri.port
52
54
  " -p #{port}" if port
53
55
  end
54
56
 
55
57
  # @since 0.7.0
56
58
  # @api private
57
59
  def username
58
- username = query['user'] || @uri.user
60
+ username = query["user"] || @uri.user
59
61
  " -U #{username}" if username
60
62
  end
61
63
 
62
64
  # @since 0.7.0
63
65
  # @api private
64
66
  def configure_password
65
- password = query['password'] || @uri.password
66
- ENV[PASSWORD] = CGI.unescape(query['password'] || @uri.password) if password
67
+ password = query["password"] || @uri.password
68
+ ENV[PASSWORD] = CGI.unescape(query["password"] || @uri.password) if password
67
69
  end
68
70
 
69
71
  # @since 1.1.0