hanami-model 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bb1fca18a6f84f606883ffab656334c1b8e619f6
4
- data.tar.gz: 8d2884fbd3bfda01dc6390b4271280cfffe1abc2
3
+ metadata.gz: b42e3225f01dfdc11db772e01e9380930378b34b
4
+ data.tar.gz: 61c681f2b8dacd1867ac9b929e84f2fc2d9da120
5
5
  SHA512:
6
- metadata.gz: 4af0a977d17c5cab779ba6b7fefc91b5e94ffa927324439ed99d45fd468000289f564f151c48e1926e2e603052967077517e055735dbe719152a021ccb2c7282
7
- data.tar.gz: f4c30f92ab810dde2c9678d8352bec0787e2c446235e70a078e7a419913bf2fc59d8d0fdcd30727be296085b74f7dc89f300bb57cca463437cf94805ce2b2800
6
+ metadata.gz: c7c31f943c0af575a6b1197c87d2ce0bcf1b1ccf3e42dab6869c85580e79f5f316c62d7c52093a8b96b4fb5ace02e237723fd3fa115398247e68c409871cb592
7
+ data.tar.gz: f6b396a6f8e57b35d9a213f9d49c45610a3dc3825fc8233e83721af811bdf411b270e6d1ce8420d745d91d725ba3ca826ffa1b6dca02e4976c44de79dc74a6e8
@@ -1,6 +1,13 @@
1
1
  # Hanami::Model
2
2
  A persistence layer for Hanami
3
3
 
4
+ ## v1.0.2 - 2017-08-04
5
+ ### Fixed
6
+ - [Maurizio De Magnis] URI escape for Postgres password
7
+ - [Marion Duprey] Ensure repository to generate timestamps values even when only one between `created_at` and `updated_at` is present
8
+ - [Paweł Świątkowski] Make Postgres JSON(B) to work with Ruby arrays
9
+ - [Luca Guidi] Don't remove migrations when running `Hanami::Model::Migrator#apply` fails to dump the database
10
+
4
11
  ## v1.0.1 - 2017-06-23
5
12
  ### Fixed
6
13
  - [Kai Kuchenbecker & Marcello Rocha & Luca Guidi] Ensure `Hanami::Entity#initialize` to not serialize (into `Hash`) other entities passed as an argument
@@ -22,8 +22,8 @@ Gem::Specification.new do |spec|
22
22
 
23
23
  spec.add_runtime_dependency 'hanami-utils', '~> 1.0'
24
24
  spec.add_runtime_dependency 'rom-sql', '~> 1.3'
25
- spec.add_runtime_dependency 'rom-repository', '~> 1.3'
26
- spec.add_runtime_dependency 'dry-types', '~> 0.10'
25
+ spec.add_runtime_dependency 'rom-repository', '~> 1.4'
26
+ spec.add_runtime_dependency 'dry-types', '~> 0.11'
27
27
  spec.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
28
28
 
29
29
  spec.add_development_dependency 'bundler', '~> 1.6'
@@ -1,5 +1,6 @@
1
1
  require 'uri'
2
2
  require 'shellwords'
3
+ require 'open3'
3
4
 
4
5
  module Hanami
5
6
  module Model
@@ -176,6 +177,15 @@ module Hanami
176
177
  def escape(string)
177
178
  Shellwords.escape(string) unless string.nil?
178
179
  end
180
+
181
+ # @since 1.0.2
182
+ # @api private
183
+ def execute(command, env: {}, error: ->(err) { raise MigrationError.new(err) })
184
+ Open3.popen3(env, command) do |_, stdout, stderr, wait_thr|
185
+ error.call(stderr.read) unless wait_thr.value.success?
186
+ yield stdout if block_given?
187
+ end
188
+ end
179
189
  end
180
190
  end
181
191
  end
@@ -47,7 +47,6 @@ module Hanami
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,18 +54,11 @@ 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.7.0
65
- # @api private
66
- def set_environment_variables
67
- ENV[PASSWORD] = password unless password.nil?
68
- end
69
-
70
62
  # @since 0.7.0
71
63
  # @api private
72
64
  def password
@@ -76,19 +68,19 @@ module Hanami
76
68
  # @since 0.4.0
77
69
  # @api private
78
70
  def dump_structure
79
- system "mysqldump --user=#{username} --no-data --skip-comments --ignore-table=#{database}.#{migrations_table} #{database} > #{schema}"
71
+ execute "mysqldump --host=#{host} --port=#{port} --user=#{username} --no-data --skip-comments --ignore-table=#{database}.#{migrations_table} #{database} > #{schema}", env: { PASSWORD => password }
80
72
  end
81
73
 
82
74
  # @since 0.4.0
83
75
  # @api private
84
76
  def load_structure
85
- system "mysql --user=#{username} #{database} < #{escape(schema)}" if schema.exist?
77
+ execute("mysql --host=#{host} --port=#{port} --user=#{username} #{database} < #{escape(schema)}", env: { PASSWORD => password }) if schema.exist?
86
78
  end
87
79
 
88
80
  # @since 0.4.0
89
81
  # @api private
90
82
  def dump_migrations_data
91
- system "mysqldump --user=#{username} --skip-comments #{database} #{migrations_table} >> #{schema}"
83
+ execute "mysqldump --host=#{host} --port=#{port} --user=#{username} --skip-comments #{database} #{migrations_table} >> #{schema}", env: { PASSWORD => password }
92
84
  end
93
85
  end
94
86
  end
@@ -89,19 +89,20 @@ module Hanami
89
89
  # @since 0.4.0
90
90
  # @api private
91
91
  def dump_structure
92
- system "pg_dump -s -x -O -T #{migrations_table} -f #{escape(schema)} #{database}"
92
+ execute "pg_dump -s -x -O -T #{migrations_table} -f #{escape(schema)} #{database}"
93
93
  end
94
94
 
95
95
  # @since 0.4.0
96
96
  # @api private
97
97
  def load_structure
98
- system "psql -X -q -f #{escape(schema)} #{database}" if schema.exist?
98
+ execute "psql -X -q -f #{escape(schema)} #{database}" if schema.exist?
99
99
  end
100
100
 
101
101
  # @since 0.4.0
102
102
  # @api private
103
103
  def dump_migrations_data
104
- system "pg_dump -t #{migrations_table} #{database} >> #{escape(schema)}"
104
+ error = ->(err) { raise MigrationError.new(err) unless err =~ /no matching tables/i }
105
+ execute "pg_dump -t #{migrations_table} #{database} >> #{escape(schema)}", error: error
105
106
  end
106
107
 
107
108
  # @since 0.5.1
@@ -1,5 +1,6 @@
1
1
  require 'pathname'
2
2
  require 'hanami/utils'
3
+ require 'English'
3
4
 
4
5
  module Hanami
5
6
  module Model
@@ -91,20 +92,36 @@ module Hanami
91
92
  # @since 0.4.0
92
93
  # @api private
93
94
  def dump_structure
94
- system "sqlite3 #{escape(path)} .schema > #{escape(schema)}"
95
+ execute "sqlite3 #{escape(path)} .schema > #{escape(schema)}"
95
96
  end
96
97
 
97
98
  # @since 0.4.0
98
99
  # @api private
99
100
  def load_structure
100
- system "sqlite3 #{escape(path)} < #{escape(schema)}" if schema.exist?
101
+ execute "sqlite3 #{escape(path)} < #{escape(schema)}" if schema.exist?
101
102
  end
102
103
 
103
104
  # @since 0.4.0
104
105
  # @api private
106
+ #
107
+ # rubocop:disable Metrics/AbcSize
108
+ # rubocop:disable Metrics/MethodLength
105
109
  def dump_migrations_data
106
- system %(sqlite3 #{escape(path)} .dump | grep '^INSERT INTO "#{migrations_table}"' >> #{escape(schema)})
110
+ execute "sqlite3 #{escape(path)} .dump" do |stdout|
111
+ begin
112
+ contents = stdout.read.split($INPUT_RECORD_SEPARATOR)
113
+ contents = contents.grep(/^INSERT INTO "#{migrations_table}"/)
114
+
115
+ ::File.open(schema, ::File::CREAT | ::File::BINARY | ::File::WRONLY | ::File::APPEND) do |file|
116
+ file.write(contents.join($INPUT_RECORD_SEPARATOR))
117
+ end
118
+ rescue => exception
119
+ raise MigrationError.new(exception.message)
120
+ end
121
+ end
107
122
  end
123
+ # rubocop:enable Metrics/MethodLength
124
+ # rubocop:enable Metrics/AbcSize
108
125
  end
109
126
  end
110
127
  end
@@ -23,8 +23,7 @@ module Hanami
23
23
  # @api private
24
24
  def initialize(relation, input)
25
25
  super
26
- columns = relation.columns.sort
27
- @timestamps = (columns & TIMESTAMPS) == TIMESTAMPS
26
+ @timestamps = relation.columns & TIMESTAMPS
28
27
  end
29
28
 
30
29
  # Processes the input
@@ -49,7 +48,7 @@ module Hanami
49
48
  # @since 0.7.0
50
49
  # @api private
51
50
  def timestamps?
52
- @timestamps
51
+ !@timestamps.empty?
53
52
  end
54
53
  end
55
54
 
@@ -63,7 +62,7 @@ module Hanami
63
62
  # @since 0.7.0
64
63
  # @api private
65
64
  def _touch(value, now)
66
- value[:updated_at] ||= now
65
+ value[:updated_at] ||= now if @timestamps.include?(:updated_at)
67
66
  value
68
67
  end
69
68
  end
@@ -79,7 +78,7 @@ module Hanami
79
78
  # @api private
80
79
  def _touch(value, now)
81
80
  super
82
- value[:created_at] ||= now
81
+ value[:created_at] ||= now if @timestamps.include?(:created_at)
83
82
  value
84
83
  end
85
84
  end
@@ -1,4 +1,5 @@
1
1
  require_relative 'abstract'
2
+ require 'cgi'
2
3
 
3
4
  module Hanami
4
5
  module Model
@@ -59,7 +60,7 @@ module Hanami
59
60
  # @since 0.7.0
60
61
  # @api private
61
62
  def configure_password
62
- ENV[PASSWORD] = @uri.password unless @uri.password.nil?
63
+ ENV[PASSWORD] = CGI.unescape(@uri.password) unless @uri.password.nil?
63
64
  end
64
65
  end
65
66
  end
@@ -31,6 +31,8 @@ module Hanami
31
31
  Array = Types::Strict::Nil | Types::Array.constructor(Coercions.method(:array))
32
32
  Hash = Types::Strict::Nil | Types::Hash.constructor(Coercions.method(:hash))
33
33
 
34
+ PG_JSON = Types::Strict::Nil | Types::Any.constructor(Coercions.method(:pg_json))
35
+
34
36
  # @since 0.7.0
35
37
  # @api private
36
38
  MAPPING = {
@@ -72,14 +74,23 @@ module Hanami
72
74
  #
73
75
  # MAPPING.fetch(unwrapped.pristine, attribute)
74
76
  MAPPING.fetch(unwrapped.pristine) do
75
- if defined?(ROM::SQL::Types::PG::JSONB) && unwrapped.pristine == ROM::SQL::Types::PG::JSONB
76
- Schema::Hash
77
+ if pg_json?(unwrapped.pristine)
78
+ Schema::PG_JSON
77
79
  else
78
80
  attribute
79
81
  end
80
82
  end
81
83
  end
82
84
 
85
+ # @since 1.0.2
86
+ # @api private
87
+ def self.pg_json?(pristine)
88
+ (defined?(ROM::SQL::Types::PG::JSONB) && pristine == ROM::SQL::Types::PG::JSONB) ||
89
+ (defined?(ROM::SQL::Types::PG::JSON) && pristine == ROM::SQL::Types::PG::JSON)
90
+ end
91
+
92
+ private_class_method :pg_json?
93
+
83
94
  # Coercer for SQL associations target
84
95
  #
85
96
  # @since 0.7.0
@@ -11,6 +11,7 @@ module Hanami
11
11
  # @since 0.7.0
12
12
  # @api private
13
13
  #
14
+ # rubocop:disable Metrics/ModuleLength
14
15
  # rubocop:disable Metrics/MethodLength
15
16
  module Coercions
16
17
  # Coerces given argument into Integer
@@ -192,8 +193,30 @@ module Hanami
192
193
  raise ArgumentError.new("invalid value for Hash(): #{arg.inspect}")
193
194
  end
194
195
  end
196
+
197
+ # Coerces given argument to appropriate Postgres JSON(B) type, i.e. Hash or Array
198
+ #
199
+ # @param arg [Object] the object to coerce
200
+ #
201
+ # @return [Hash, Array] the result of the coercion
202
+ #
203
+ # @raise [ArgumentError] if the coercion fails
204
+ #
205
+ # @since 1.0.2
206
+ # @api private
207
+ def self.pg_json(arg)
208
+ case arg
209
+ when ->(a) { a.respond_to?(:to_hash) }
210
+ hash(arg)
211
+ when ->(a) { a.respond_to?(:to_a) }
212
+ array(arg)
213
+ else
214
+ raise ArgumentError.new("invalid value for PG_JSON(): #{arg.inspect}")
215
+ end
216
+ end
195
217
  end
196
218
  # rubocop:enable Metrics/MethodLength
219
+ # rubocop:enable Metrics/ModuleLength
197
220
  end
198
221
  end
199
222
  end
@@ -3,6 +3,6 @@ module Hanami
3
3
  # Defines the version
4
4
  #
5
5
  # @since 0.1.0
6
- VERSION = '1.0.1'.freeze
6
+ VERSION = '1.0.2'.freeze
7
7
  end
8
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hanami-model
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luca Guidi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-06-23 00:00:00.000000000 Z
11
+ date: 2017-08-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hanami-utils
@@ -44,28 +44,28 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '1.3'
47
+ version: '1.4'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '1.3'
54
+ version: '1.4'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: dry-types
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0.10'
61
+ version: '0.11'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0.10'
68
+ version: '0.11'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: concurrent-ruby
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -192,7 +192,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
192
192
  version: '0'
193
193
  requirements: []
194
194
  rubyforge_project:
195
- rubygems_version: 2.6.12
195
+ rubygems_version: 2.6.11
196
196
  signing_key:
197
197
  specification_version: 4
198
198
  summary: A persistence layer for Hanami