rom-rails 0.3.3 → 0.4.0

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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -0
  3. data/lib/generators/rom/relation/templates/relation.rb.erb +3 -3
  4. data/lib/generators/rom/relation_generator.rb +5 -5
  5. data/lib/rom/rails/active_record/configuration.rb +1 -1
  6. data/lib/rom/rails/configuration.rb +6 -1
  7. data/lib/rom/rails/inflections.rb +2 -1
  8. data/lib/rom/rails/model/form/class_interface.rb +2 -2
  9. data/lib/rom/rails/model/validator.rb +75 -5
  10. data/lib/rom/rails/model/validator/uniqueness_validator.rb +7 -3
  11. data/lib/rom/rails/railtie.rb +68 -64
  12. data/lib/rom/rails/tasks/db.rake +3 -7
  13. data/lib/rom/rails/version.rb +1 -1
  14. data/rom-rails.gemspec +1 -1
  15. data/spec/dummy/app/mappers/users.rb +1 -0
  16. data/spec/dummy/app/relations/dummy_relation.rb +1 -1
  17. data/spec/dummy/config/initializers/rom.rb +2 -2
  18. data/spec/dummy/db/migrate/20141110205016_add_users.rb +1 -0
  19. data/spec/dummy/lib/rom/test_adapter.rb +1 -1
  20. data/spec/{dummy/spec/features → features}/users_spec.rb +0 -0
  21. data/spec/{dummy/spec/integration → integration}/activerecord_setup.rb +1 -1
  22. data/spec/{dummy/spec/integration → integration}/form_with_injected_commands_spec.rb +0 -0
  23. data/spec/integration/initializer_spec.rb +11 -0
  24. data/spec/{dummy/spec/integration → integration}/logger_spec.rb +3 -3
  25. data/spec/{dummy/spec/integration → integration}/new_user_form_spec.rb +0 -0
  26. data/spec/{dummy/spec/integration → integration}/user_attributes_spec.rb +0 -0
  27. data/spec/{dummy/spec/integration → integration}/user_commands_spec.rb +0 -0
  28. data/spec/{dummy/spec/integration → integration}/user_model_mapping_spec.rb +0 -0
  29. data/spec/lib/generators/relation_generator_spec.rb +4 -4
  30. data/spec/spec_helper.rb +2 -2
  31. data/spec/unit/validator/embedded_spec.rb +118 -0
  32. data/spec/unit/validator_spec.rb +68 -3
  33. metadata +26 -24
  34. data/spec/dummy/spec/integration/initializer_spec.rb +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d53fad0999e88095261c12196674bf05ef9754eb
4
- data.tar.gz: 134a8100ee7b0b4967315b3ce342a1526b9280e8
3
+ metadata.gz: 3eb6dc7cbe13616ba42eb04dedbf46d454e5fee6
4
+ data.tar.gz: 3de6f1246346eda7234baec3ada3af5ee425603a
5
5
  SHA512:
6
- metadata.gz: ff2d1b07af8d3e2ad0263ea1f7fa1d433dc1f3da4c801710f51daa778dc9b903224747bdd55a9ca9bb232103a4561ef332161a6db12bd367a9ffe6e130fd127b
7
- data.tar.gz: ebf50d904cd43b7be7addd0c9167921e4ae9924250b8e69aea2bbd8f9f705020f80865fc5336b8a1ee67480b8b6833d241250cb3c732afc4ed2138d2bf67c571
6
+ metadata.gz: 7f4f2a71ecc81887fee3c3fd326119af378d3a633dcadcab03191b7410756bbcf39db3ba65d5e1b78e498469299c0de1511b340cd0af861a99ae76a8b3085b64
7
+ data.tar.gz: 89106263fa73c8346e2eb7b0062e1c7386c7995e0a9630ae490b40adf5f6e7fde3e6f943b1b1c07d36154469f9f8bf94d0b4004f5dd6e5b8f0e26487a2ab16b6
data/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ ## v0.4.0 2015-06-22
2
+
3
+ ### Added
4
+
5
+ * `embedded` validators allowing to nest validations for embedded values (solnic)
6
+ * Uniqueness validation supports `:scope` option (vrish88)
7
+
8
+ ### Changed
9
+
10
+ * `ROM.env` is lazy-finalized on first access of any of the components (solnic)
11
+ * `db:setup` provided by the railtie now loads `:environment` (solnic)
12
+ * `repositories` in railtie configuration deprecated in favor of `gateways` (solnic)
13
+
14
+ ### Fixed
15
+
16
+ * `method_missing` in validators behaves properly now (solnic)
17
+
18
+ [Compare v0.3.3...v0.4.0](https://github.com/rom-rb/rom-rails/compare/v0.3.3...v0.4.0)
19
+
1
20
  ## v0.3.3 2015-05-22
2
21
 
3
22
  ### Added
@@ -1,8 +1,8 @@
1
1
  class <%= class_name %>Relation < ROM::Relation<%= "[:#{adapter}]" %>
2
- <% if repository -%>
3
- repository :<%= repository %>
2
+ <% if gateway -%>
3
+ gateway :<%= gateway %>
4
4
  <% else -%>
5
- # repository :default
5
+ # gateway :default
6
6
  <% end -%>
7
7
 
8
8
  dataset :<%= dataset %>
@@ -8,9 +8,9 @@ module ROM
8
8
  desc: "specify an adapter to use", required: true,
9
9
  default: ROM.adapters.keys.first
10
10
 
11
- class_option :repository,
12
- banner: "--repository=repo",
13
- desc: "specify a repository to connect to",
11
+ class_option :gateway,
12
+ banner: "--gateway=repo",
13
+ desc: "specify a gateway to connect to",
14
14
  required: false
15
15
 
16
16
  class_option :register,
@@ -40,8 +40,8 @@ module ROM
40
40
  options[:register] || dataset
41
41
  end
42
42
 
43
- def repository
44
- options[:repository]
43
+ def gateway
44
+ options[:gateway]
45
45
  end
46
46
 
47
47
  end
@@ -17,7 +17,7 @@ module ROM
17
17
  :root
18
18
  ].freeze
19
19
 
20
- # Returns repository configuration for the current environment.
20
+ # Returns gateway configuration for the current environment.
21
21
  #
22
22
  # @note This relies on ActiveRecord being initialized already.
23
23
  # @param [Rails::Application]
@@ -1,11 +1,16 @@
1
1
  require 'virtus'
2
+ require 'rom/support/deprecations'
2
3
 
3
4
  module ROM
4
5
  module Rails
5
6
  class Configuration
7
+ extend ROM::Deprecations
8
+
6
9
  include Virtus.model(strict: true)
7
10
 
8
- attribute :repositories, Hash, default: {}
11
+ attribute :gateways, Hash, default: {}
12
+
13
+ deprecate :repositories, :gateways
9
14
  end
10
15
  end
11
16
  end
@@ -1,5 +1,6 @@
1
1
  require 'active_support/inflections'
2
2
 
3
3
  ActiveSupport::Inflector.inflections do |inflect|
4
- inflect.acronym 'ROM'
4
+ # `acronym` was added in ActiveSupport 3.2.5
5
+ inflect.acronym 'ROM' if inflect.respond_to?(:acronym)
5
6
  end
@@ -431,8 +431,8 @@ module ROM
431
431
  klass.validator @validator
432
432
 
433
433
  relation = rom.relations[rel_name]
434
- repository = rom.repositories[relation.repository]
435
- repository.extend_command_class(klass, relation.dataset)
434
+ gateway = rom.gateways[relation.gateway]
435
+ gateway.extend_command_class(klass, relation.dataset)
436
436
 
437
437
  klass.build(relation)
438
438
  end
@@ -1,4 +1,5 @@
1
1
  require 'rom/rails/model/validator/uniqueness_validator'
2
+ require 'rom/support/class_macros'
2
3
 
3
4
  module ROM
4
5
  module Model
@@ -30,8 +31,14 @@ module ROM
30
31
  def self.included(base)
31
32
  base.class_eval do
32
33
  extend ClassMethods
34
+ extend ROM::ClassMacros
35
+
33
36
  include ActiveModel::Validations
34
37
  include Equalizer.new(:attributes, :errors)
38
+
39
+ base.defines :embedded_validators
40
+
41
+ embedded_validators({})
35
42
  end
36
43
  end
37
44
 
@@ -40,11 +47,15 @@ module ROM
40
47
  # @api private
41
48
  attr_reader :attributes
42
49
 
50
+ # @api private
51
+ attr_reader :attr_names
52
+
43
53
  delegate :model_name, to: :attributes
44
54
 
45
55
  # @api private
46
56
  def initialize(attributes)
47
57
  @attributes = attributes
58
+ @attr_names = self.class.validators.map(&:attributes).flatten.uniq
48
59
  end
49
60
 
50
61
  # @return [Model::Attributes]
@@ -72,8 +83,12 @@ module ROM
72
83
  # as it expects the object to provide attribute values. Meh.
73
84
  #
74
85
  # @api private
75
- def method_missing(name)
76
- attributes[name]
86
+ def method_missing(name, *args, &block)
87
+ if attr_names.include?(name)
88
+ attributes[name]
89
+ else
90
+ super
91
+ end
77
92
  end
78
93
 
79
94
  module ClassMethods
@@ -99,9 +114,13 @@ module ROM
99
114
  @relation
100
115
  end
101
116
 
102
- # FIXME: this looks like not needed
103
- def model_name
104
- attributes.model_name
117
+ # @api private
118
+ def set_model_name(name)
119
+ class_eval <<-RUBY
120
+ def self.model_name
121
+ @model_name ||= ActiveModel::Name.new(self, nil, #{name.inspect})
122
+ end
123
+ RUBY
105
124
  end
106
125
 
107
126
  # Trigger validation for specific attributes
@@ -115,6 +134,57 @@ module ROM
115
134
  validator = new(attributes)
116
135
  validator.call
117
136
  end
137
+
138
+ # Specify an embedded validator for nested structures
139
+ #
140
+ # @example
141
+ # class UserValidator
142
+ # include ROM::Model::Validator
143
+ #
144
+ # set_model_name 'User'
145
+ #
146
+ # embedded :address do
147
+ # validates :city, :street, :zipcode, presence: true
148
+ # end
149
+ #
150
+ # emebdded :tasks do
151
+ # validates :title, presence: true
152
+ # end
153
+ # end
154
+ #
155
+ # validator = UserAttributes.new(address: {}, tasks: {})
156
+ #
157
+ # validator.valid? # false
158
+ # validator.errors[:address].first # errors for address
159
+ # validator.errors[:tasks] # errors for tasks
160
+ #
161
+ # @api public
162
+ def embedded(name, &block)
163
+ validator_class = Class.new { include ROM::Model::Validator }
164
+ validator_class.class_eval(&block)
165
+ validator_class.set_model_name(name.to_s.classify)
166
+
167
+ embedded_validators[name] = validator_class
168
+
169
+ validates name, presence: true
170
+
171
+ validate do
172
+ value = attributes[name]
173
+
174
+ if value.present?
175
+ Array([value]).flatten.each do |object|
176
+ validator = validator_class.new(object)
177
+ validator.validate
178
+
179
+ if validator.errors.any?
180
+ errors.add(name, validator.errors)
181
+ else
182
+ errors.add(name, [])
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
118
188
  end
119
189
  end
120
190
  end
@@ -24,13 +24,17 @@ module ROM
24
24
  super
25
25
  @klass = options.fetch(:class)
26
26
  @message = options.fetch(:message) { :taken }
27
+ @scope_keys = options[:scope]
27
28
  end
28
29
 
29
30
  # Hook called by ActiveModel internally
30
31
  #
31
32
  # @api private
32
33
  def validate_each(validator, name, value)
33
- validator.errors.add(name, message) unless unique?(name, value)
34
+ scope = Array(@scope_keys).each_with_object({}) do |key, scope|
35
+ scope[key] = validator.to_model[key]
36
+ end
37
+ validator.errors.add(name, message) unless unique?(name, value, scope)
34
38
  end
35
39
 
36
40
  private
@@ -66,8 +70,8 @@ module ROM
66
70
  # @return [TrueClass,FalseClass]
67
71
  #
68
72
  # @api private
69
- def unique?(name, value)
70
- relation.unique?(name => value)
73
+ def unique?(name, value, scope)
74
+ relation.unique?({name => value}.merge(scope))
71
75
  end
72
76
  end
73
77
  end
@@ -12,42 +12,11 @@ module ROM
12
12
  class Railtie < ::Rails::Railtie
13
13
  COMPONENT_DIRS = %w(relations mappers commands).freeze
14
14
 
15
- MissingRepositoryConfigError = Class.new(StandardError)
15
+ MissingGatewayConfigError = Class.new(StandardError)
16
16
 
17
- # @api public
18
- def self.setup_repositories
19
- raise(
20
- MissingRepositoryConfigError,
21
- "seems like you didn't configure any repositories"
22
- ) unless config.rom.repositories.any?
23
-
24
- ROM.setup(config.rom.repositories)
25
- self
26
- end
27
-
28
- # @api public
29
- def self.finalize
30
- ROM.finalize
31
- self
32
- end
33
-
34
- # If there's no default repository configured, try to infer it from
35
- # other sources, e.g. ActiveRecord.
36
- #
37
- # @api private
38
- def self.infer_default_repository
39
- return unless active_record?
40
- spec = ROM::Rails::ActiveRecord::Configuration.call
41
- [:sql, spec[:uri], spec[:options]]
42
- end
43
-
44
- # @api private
45
- def self.active_record?
46
- defined?(::ActiveRecord)
47
- end
48
-
49
- # @api private
50
- def before_initialize
17
+ # Make `ROM::Rails::Configuration` instance available to the user via
18
+ # `Rails.application.config` before other initializers run.
19
+ config.before_initialize do |_app|
51
20
  config.rom = Configuration.new
52
21
  end
53
22
 
@@ -59,25 +28,19 @@ module ROM
59
28
 
60
29
  initializer 'rom.adjust_eager_load_paths' do |app|
61
30
  paths = COMPONENT_DIRS.map do |directory|
62
- ::Rails.root.join('app', directory).to_s
31
+ root.join('app', directory).to_s
63
32
  end
64
33
 
65
- app.config.eager_load_paths -= paths
34
+ app.config.eager_load_paths -= paths
66
35
  end
67
36
 
68
37
  rake_tasks do
69
- load "rom/rails/tasks/db.rake" unless self.class.active_record?
70
- end
71
-
72
- # Make `ROM::Rails::Configuration` instance available to the user via
73
- # `Rails.application.config` before other initializers run.
74
- config.before_initialize do |_app|
75
- before_initialize
38
+ load "rom/rails/tasks/db.rake" unless active_record?
76
39
  end
77
40
 
78
41
  # Reload ROM-related application code on each request.
79
42
  config.to_prepare do |_config|
80
- Railtie.setup
43
+ Railtie.finalize
81
44
  end
82
45
 
83
46
  # Behaves like `Railtie#configure` if the given block does not take any
@@ -85,11 +48,13 @@ module ROM
85
48
  #
86
49
  # @example
87
50
  # ROM::Rails::Railtie.configure do |config|
88
- # config.repositories[:default] = [:yaml, 'yaml:///data']
51
+ # config.gateways[:default] = [:yaml, 'yaml:///data']
89
52
  # end
90
53
  #
91
54
  # @api public
92
55
  def configure(&block)
56
+ config.rom = Configuration.new unless config.respond_to?(:rom)
57
+
93
58
  if block.arity == 1
94
59
  block.call(config.rom)
95
60
  else
@@ -97,40 +62,69 @@ module ROM
97
62
  end
98
63
  end
99
64
 
65
+ # @api private
66
+ def setup(gateways)
67
+ raise(
68
+ MissingGatewayConfigError,
69
+ "seems like you didn't configure any gateways"
70
+ ) unless gateways.any?
71
+
72
+ ROM.setup(gateways)
73
+ end
74
+
75
+ # @api private
76
+ def setup_gateways
77
+ gateways =
78
+ if env
79
+ env.gateways
80
+ else
81
+ prepare_gateways
82
+ end
83
+
84
+ setup(gateways)
85
+ end
86
+
87
+ # @api private
88
+ def finalize
89
+ setup_gateways
90
+ load_components
91
+ ROM.finalize
92
+ end
93
+
100
94
  # TODO: Add `ROM.env.disconnect` to core.
101
95
  #
102
96
  # @api private
103
97
  def disconnect
104
- ROM.env.repositories.each_value(&:disconnect)
98
+ ROM.gateways.each_key(&:disconnect)
105
99
  end
106
100
 
107
101
  # @api private
108
- def setup
109
- if ROM.env
110
- ROM.setup(ROM.env.repositories)
111
- else
112
- repositories = config.rom.repositories
102
+ def prepare_gateways
103
+ config.rom.gateways[:default] ||= infer_default_gateway if active_record?
104
+ config.rom.gateways
105
+ end
113
106
 
114
- if self.class.active_record?
115
- repositories[:default] ||= self.class.infer_default_repository
116
- end
107
+ # If there's no default gateway configured, try to infer it from
108
+ # other sources, e.g. ActiveRecord.
109
+ #
110
+ # @api private
111
+ def infer_default_gateway
112
+ spec = ROM::Rails::ActiveRecord::Configuration.call
113
+ [:sql, spec[:uri], spec[:options]]
114
+ end
117
115
 
118
- self.class.setup_repositories
119
- end
120
- load_all
121
- self.class.finalize
116
+ def load_initializer
117
+ load "#{root}/config/initializers/rom.rb" rescue LoadError
122
118
  end
123
119
 
124
120
  # @api private
125
- def load_all
126
- COMPONENT_DIRS.each do |type|
127
- load_files(type)
128
- end
121
+ def load_components
122
+ COMPONENT_DIRS.each { |type| load_files(type) }
129
123
  end
130
124
 
131
125
  # @api private
132
126
  def load_files(type)
133
- Dir[root.join("app/#{type}/**/*.rb").to_s].each do |path|
127
+ Dir[root.join("app/#{type}/**/*.rb")].each do |path|
134
128
  require_dependency(path)
135
129
  end
136
130
  end
@@ -139,6 +133,16 @@ module ROM
139
133
  def root
140
134
  ::Rails.root
141
135
  end
136
+
137
+ # @api private
138
+ def env
139
+ ROM.env
140
+ end
141
+
142
+ # @api private
143
+ def active_record?
144
+ defined?(::ActiveRecord)
145
+ end
142
146
  end
143
147
  end
144
148
  end
@@ -1,11 +1,7 @@
1
1
  namespace :db do
2
- desc 'Set up ROM repositories'
2
+ desc 'Set up ROM gateways'
3
3
  task :setup do
4
- railtie = ROM::Rails::Railtie
5
- railtie.before_initialize
6
-
7
- require "#{Rails.root}/config/initializers/rom"
8
-
9
- railtie.setup_repositories.finalize
4
+ ROM::Rails::Railtie.load_initializer
5
+ ROM::Rails::Railtie.setup_gateways
10
6
  end
11
7
  end
@@ -1,5 +1,5 @@
1
1
  module ROM
2
2
  module Rails
3
- VERSION = '0.3.3'.freeze
3
+ VERSION = '0.4.0'.freeze
4
4
  end
5
5
  end
data/rom-rails.gemspec CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
  spec.require_paths = ["lib"]
19
19
 
20
- spec.add_runtime_dependency 'rom', '~> 0.7', '>= 0.7.0'
20
+ spec.add_runtime_dependency 'rom', '~> 0.8', '>= 0.8.0'
21
21
  spec.add_runtime_dependency 'addressable', '~> 2.3'
22
22
  spec.add_runtime_dependency 'charlatan', '~> 0.1'
23
23
  spec.add_runtime_dependency 'virtus', '~> 1.0', '>= 1.0.5'
@@ -7,4 +7,5 @@ class UserMapper < ROM::Mapper
7
7
  attribute :id
8
8
  attribute :name
9
9
  attribute :email
10
+ attribute :birthday
10
11
  end
@@ -1,4 +1,4 @@
1
1
  class DummyRelation < ROM::Relation[:test_adapter]
2
2
  register_as :dummy
3
- repository :test
3
+ gateway :test
4
4
  end
@@ -1,5 +1,5 @@
1
1
  ROM::Rails::Railtie.configure do |config|
2
2
  scheme = RUBY_ENGINE == 'jruby' ? 'jdbc:sqlite' : 'sqlite'
3
- config.repositories[:default] = [:sql, "#{scheme}://#{Rails.root}/db/#{Rails.env}.sqlite3"]
4
- config.repositories[:test] = [:test_adapter, foo: :bar]
3
+ config.gateways[:default] = [:sql, "#{scheme}://#{Rails.root}/db/#{Rails.env}.sqlite3"]
4
+ config.gateways[:test] = [:test_adapter, foo: :bar]
5
5
  end
@@ -4,6 +4,7 @@ ROM::SQL.migration do
4
4
  primary_key :id
5
5
  String :name
6
6
  String :email
7
+ Date :birthday
7
8
  DateTime :created_at
8
9
  DateTime :updated_at
9
10
  end
@@ -3,7 +3,7 @@ module ROM
3
3
  class Relation < ROM::Relation
4
4
  end
5
5
 
6
- class Repository < ROM::Repository
6
+ class Gateway < ROM::Gateway
7
7
  include Equalizer.new(:args)
8
8
 
9
9
  attr_reader :args, :datasets
@@ -1,4 +1,4 @@
1
- describe 'Connects to repositories using database.yml' do
1
+ describe 'Connects to gateways using database.yml' do
2
2
  it 'works' do
3
3
  pending 'I have no idea how to load AR in isolation'
4
4
  raise 'database.yml must go'
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'ROM initializer' do
4
+ it 'allows setting up a custom gateway' do
5
+ gateway = ROM::TestAdapter::Gateway.new(foo: :bar)
6
+ relation = DummyRelation.new([])
7
+
8
+ expect(rom.gateways[:test]).to eql(gateway)
9
+ expect(rom.relations.dummy).to eql(relation)
10
+ end
11
+ end
@@ -3,12 +3,12 @@ require 'spec_helper'
3
3
  describe 'ROM logger' do
4
4
  let(:rom) { ROM.env }
5
5
 
6
- it 'sets up rails logger for all repositories' do
6
+ it 'sets up rails logger for all gateways' do
7
7
  pending 'this will be re-enabled once we have feature detection on ' \
8
8
  'adapters and in case of missing rails-log-subscriber support we ' \
9
9
  'will set logger to Rails.logger'
10
- rom.repositories.each_value do |repository|
11
- expect(repository.logger).to be(Rails.logger)
10
+ rom.gateways.each_value do |gateway|
11
+ expect(gateway.logger).to be(Rails.logger)
12
12
  end
13
13
  end
14
14
  end
@@ -19,7 +19,7 @@ describe ROM::Generators::RelationGenerator, type: :generator do
19
19
  file 'users_relation.rb' do
20
20
  contains <<-CONTENT.strip_heredoc
21
21
  class UsersRelation < ROM::Relation[:#{default_adapter}]
22
- # repository :default
22
+ # gateway :default
23
23
 
24
24
  dataset :users
25
25
 
@@ -45,11 +45,11 @@ describe ROM::Generators::RelationGenerator, type: :generator do
45
45
  expect(relation).to include("class UsersRelation < ROM::Relation[:memory]")
46
46
  end
47
47
 
48
- specify "with given repository" do
49
- run_generator ['users', '--repository=remote']
48
+ specify "with given gateway" do
49
+ run_generator ['users', '--gateway=remote']
50
50
 
51
51
  relation = File.read(File.join(destination_root, 'app', 'relations', 'users_relation.rb'))
52
- expect(relation).to include("repository :remote")
52
+ expect(relation).to include("gateway :remote")
53
53
  end
54
54
 
55
55
  specify "with given registration" do
data/spec/spec_helper.rb CHANGED
@@ -24,14 +24,14 @@ RSpec.configure do |config|
24
24
  config.order = "random"
25
25
 
26
26
  config.before(:suite) do
27
- conn = ROM.env.repositories[:default].connection
27
+ conn = ROM.env.gateways[:default].connection
28
28
 
29
29
  DatabaseCleaner[:sequel, connection: conn].strategy = :transaction
30
30
  DatabaseCleaner[:sequel, connection: conn].clean_with(:truncation)
31
31
  end
32
32
 
33
33
  config.around(:each) do |example|
34
- conn = ROM.env.repositories[:default].connection
34
+ conn = ROM.env.gateways[:default].connection
35
35
 
36
36
  DatabaseCleaner[:sequel, connection: conn].cleaning { example.run }
37
37
  end
@@ -0,0 +1,118 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Embedded validators' do
4
+ it 'allows defining a validator for a nested hash' do
5
+ user_validator = Class.new do
6
+ include ROM::Model::Validator
7
+
8
+ set_model_name 'User'
9
+
10
+ validates :name, presence: true
11
+
12
+ embedded :address do
13
+ set_model_name 'Address'
14
+
15
+ validates :street, :city, :zipcode, presence: true
16
+ end
17
+ end
18
+
19
+ attributes = { name: '', address: { street: '', city: '', zipcode: '' } }
20
+
21
+ expect { user_validator.call(attributes) }.to raise_error(
22
+ ROM::Model::ValidationError)
23
+
24
+ validator = user_validator.new(attributes)
25
+
26
+ expect(validator).to_not be_valid
27
+
28
+ expect(validator.errors[:name]).to include("can't be blank")
29
+
30
+ address_errors = validator.errors[:address].first
31
+
32
+ expect(address_errors).to_not be_empty
33
+
34
+ expect(address_errors[:street]).to include("can't be blank")
35
+ expect(address_errors[:city]).to include("can't be blank")
36
+ expect(address_errors[:zipcode]).to include("can't be blank")
37
+ end
38
+
39
+ it 'allows defining a validator for a nested array' do
40
+ user_validator = Class.new do
41
+ include ROM::Model::Validator
42
+
43
+ set_model_name 'User'
44
+
45
+ validates :name, presence: true
46
+
47
+ embedded :tasks do
48
+ set_model_name 'Task'
49
+
50
+ validates :title, presence: true
51
+ end
52
+ end
53
+
54
+ attributes = {
55
+ name: '',
56
+ tasks: [
57
+ { title: '' },
58
+ { title: 'Two' }
59
+ ]
60
+ }
61
+
62
+ expect { user_validator.call(attributes) }.to raise_error(
63
+ ROM::Model::ValidationError)
64
+
65
+ validator = user_validator.new(attributes)
66
+
67
+ expect(validator).to_not be_valid
68
+
69
+ expect(validator.errors[:name]).to include("can't be blank")
70
+
71
+ task_errors = validator.errors[:tasks]
72
+
73
+ expect(task_errors).to_not be_empty
74
+
75
+ expect(task_errors[0][:title]).to include("can't be blank")
76
+ expect(task_errors[1]).to be_empty
77
+ end
78
+
79
+ it 'validates presence of the nested structure' do
80
+ user_validator = Class.new do
81
+ include ROM::Model::Validator
82
+
83
+ set_model_name 'User'
84
+
85
+ validates :name, presence: true
86
+
87
+ embedded :tasks do
88
+ set_model_name 'Task'
89
+
90
+ validates :title, presence: true
91
+ end
92
+ end
93
+
94
+ validator = user_validator.new(name: '')
95
+ validator.validate
96
+
97
+ expect(validator.errors[:name]).to include("can't be blank")
98
+ expect(validator.errors[:tasks]).to include("can't be blank")
99
+ end
100
+
101
+ it 'exposes registered validators in embedded_validators hash' do
102
+ user_validator = Class.new do
103
+ include ROM::Model::Validator
104
+
105
+ set_model_name 'User'
106
+
107
+ validates :name, presence: true
108
+
109
+ embedded :tasks do
110
+ set_model_name 'Task'
111
+
112
+ validates :title, presence: true
113
+ end
114
+ end
115
+
116
+ expect(user_validator.embedded_validators[:tasks]).to be_present
117
+ end
118
+ end
@@ -11,6 +11,7 @@ describe 'Validation' do
11
11
 
12
12
  attribute :name, String
13
13
  attribute :email, String
14
+ attribute :birthday, Date
14
15
  }
15
16
  end
16
17
 
@@ -58,11 +59,9 @@ describe 'Validation' do
58
59
  describe ':uniqueness' do
59
60
  let(:attributes) { user_attrs.new(name: 'Jane', email: 'jane@doe.org') }
60
61
 
61
- before do
62
+ it 'sets default error messages' do
62
63
  rom.relations.users.insert(name: 'Jane', email: 'jane@doe.org')
63
- end
64
64
 
65
- it 'sets default error messages' do
66
65
  expect(validator).to_not be_valid
67
66
  expect(validator.errors[:email]).to eql(['has already been taken'])
68
67
  end
@@ -73,5 +72,71 @@ describe 'Validation' do
73
72
  expect(validator).to_not be_valid
74
73
  expect(validator.errors[:name]).to eql(['TAKEN!'])
75
74
  end
75
+
76
+ context 'with unique attributes within a scope' do
77
+ let(:user_validator) do
78
+ Class.new {
79
+ include ROM::Model::Validator
80
+
81
+ relation :users
82
+
83
+ validates :email, uniqueness: {scope: :name}
84
+
85
+ def self.name
86
+ 'User'
87
+ end
88
+ }
89
+ end
90
+
91
+ let(:doubly_scoped_validator) do
92
+ Class.new {
93
+ include ROM::Model::Validator
94
+
95
+ relation :users
96
+
97
+ validates :email, uniqueness: {scope: [:name, :birthday]}
98
+
99
+ def self.name
100
+ 'User'
101
+ end
102
+ }
103
+ end
104
+
105
+ it 'does not add errors' do
106
+ rom.relations.users.insert(name: 'Jane', email: 'jane+doe@doe.org')
107
+ attributes = user_attrs.new(name: 'Jane', email: 'jane@doe.org', birthday: Date.parse('2014-12-12'))
108
+ validator = user_validator.new(attributes)
109
+ expect(validator).to be_valid
110
+ end
111
+
112
+ it 'adds an error when the doubly scoped validation fails' do
113
+ attributes = user_attrs.new(name: 'Jane', email: 'jane@doe.org', birthday: Date.parse('2014-12-12'))
114
+ validator = doubly_scoped_validator.new(attributes)
115
+ expect(validator).to be_valid
116
+
117
+ rom.relations.users.insert(attributes.attributes)
118
+ expect(validator).to_not be_valid
119
+
120
+ attributes = user_attrs.new(name: 'Jane', email: 'jane+doe@doe.org', birthday: Date.parse('2014-12-12'))
121
+ validator = doubly_scoped_validator.new(attributes)
122
+ expect(validator).to be_valid
123
+ end
124
+ end
125
+ end
126
+
127
+ describe '#method_missing' do
128
+ let(:attributes) { { name: 'Jane' } }
129
+
130
+ it 'returns attribute value if present' do
131
+ expect(validator.name).to eql('Jane')
132
+ end
133
+
134
+ it 'returns nil if attribute is not present' do
135
+ expect(validator.email).to be(nil)
136
+ end
137
+
138
+ it 'raises error when name does not match any of the attributes' do
139
+ expect { validator.foobar }.to raise_error(NoMethodError, /foobar/)
140
+ end
76
141
  end
77
142
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rom-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Solnica
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-22 00:00:00.000000000 Z
11
+ date: 2015-06-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rom
@@ -16,20 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.7'
19
+ version: '0.8'
20
20
  - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: 0.7.0
22
+ version: 0.8.0
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - "~>"
28
28
  - !ruby/object:Gem::Version
29
- version: '0.7'
29
+ version: '0.8'
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 0.7.0
32
+ version: 0.8.0
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: addressable
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -241,17 +241,17 @@ files:
241
241
  - spec/dummy/public/500.html
242
242
  - spec/dummy/public/favicon.ico
243
243
  - spec/dummy/public/robots.txt
244
- - spec/dummy/spec/features/users_spec.rb
245
- - spec/dummy/spec/integration/activerecord_setup.rb
246
- - spec/dummy/spec/integration/form_with_injected_commands_spec.rb
247
- - spec/dummy/spec/integration/initializer_spec.rb
248
- - spec/dummy/spec/integration/logger_spec.rb
249
- - spec/dummy/spec/integration/new_user_form_spec.rb
250
- - spec/dummy/spec/integration/user_attributes_spec.rb
251
- - spec/dummy/spec/integration/user_commands_spec.rb
252
- - spec/dummy/spec/integration/user_model_mapping_spec.rb
253
244
  - spec/dummy/vendor/assets/javascripts/.keep
254
245
  - spec/dummy/vendor/assets/stylesheets/.keep
246
+ - spec/features/users_spec.rb
247
+ - spec/integration/activerecord_setup.rb
248
+ - spec/integration/form_with_injected_commands_spec.rb
249
+ - spec/integration/initializer_spec.rb
250
+ - spec/integration/logger_spec.rb
251
+ - spec/integration/new_user_form_spec.rb
252
+ - spec/integration/user_attributes_spec.rb
253
+ - spec/integration/user_commands_spec.rb
254
+ - spec/integration/user_model_mapping_spec.rb
255
255
  - spec/lib/active_record/configuration_spec.rb
256
256
  - spec/lib/generators/commands_generator_spec.rb
257
257
  - spec/lib/generators/form_generator_spec.rb
@@ -259,6 +259,7 @@ files:
259
259
  - spec/lib/generators/relation_generator_spec.rb
260
260
  - spec/spec_helper.rb
261
261
  - spec/unit/form_spec.rb
262
+ - spec/unit/validator/embedded_spec.rb
262
263
  - spec/unit/validator_spec.rb
263
264
  homepage: http://rom-rb.org
264
265
  licenses:
@@ -341,17 +342,17 @@ test_files:
341
342
  - spec/dummy/public/500.html
342
343
  - spec/dummy/public/favicon.ico
343
344
  - spec/dummy/public/robots.txt
344
- - spec/dummy/spec/features/users_spec.rb
345
- - spec/dummy/spec/integration/activerecord_setup.rb
346
- - spec/dummy/spec/integration/form_with_injected_commands_spec.rb
347
- - spec/dummy/spec/integration/initializer_spec.rb
348
- - spec/dummy/spec/integration/logger_spec.rb
349
- - spec/dummy/spec/integration/new_user_form_spec.rb
350
- - spec/dummy/spec/integration/user_attributes_spec.rb
351
- - spec/dummy/spec/integration/user_commands_spec.rb
352
- - spec/dummy/spec/integration/user_model_mapping_spec.rb
353
345
  - spec/dummy/vendor/assets/javascripts/.keep
354
346
  - spec/dummy/vendor/assets/stylesheets/.keep
347
+ - spec/features/users_spec.rb
348
+ - spec/integration/activerecord_setup.rb
349
+ - spec/integration/form_with_injected_commands_spec.rb
350
+ - spec/integration/initializer_spec.rb
351
+ - spec/integration/logger_spec.rb
352
+ - spec/integration/new_user_form_spec.rb
353
+ - spec/integration/user_attributes_spec.rb
354
+ - spec/integration/user_commands_spec.rb
355
+ - spec/integration/user_model_mapping_spec.rb
355
356
  - spec/lib/active_record/configuration_spec.rb
356
357
  - spec/lib/generators/commands_generator_spec.rb
357
358
  - spec/lib/generators/form_generator_spec.rb
@@ -359,4 +360,5 @@ test_files:
359
360
  - spec/lib/generators/relation_generator_spec.rb
360
361
  - spec/spec_helper.rb
361
362
  - spec/unit/form_spec.rb
363
+ - spec/unit/validator/embedded_spec.rb
362
364
  - spec/unit/validator_spec.rb
@@ -1,11 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe 'ROM initializer' do
4
- it 'allows setting up a custom repository' do
5
- repository = ROM::TestAdapter::Repository.new(foo: :bar)
6
- relation = DummyRelation.new([])
7
-
8
- expect(rom.repositories[:test]).to eql(repository)
9
- expect(rom.relations.dummy).to eql(relation)
10
- end
11
- end