reform-rails 0.1.7 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,18 +1,18 @@
1
1
  language: ruby
2
2
  cache: bundler
3
-
3
+ before_install:
4
+ - gem update --system
5
+ - gem install bundler
4
6
  rvm:
5
- - 2.3.1
6
- - 2.2.3
7
- - 2.0.0
7
+ - 2.5.5
8
+ - 2.6.3
9
+ - 2.7
8
10
  services:
9
11
  - mongodb
10
- gemfile:
11
- - gemfiles/Gemfile.rails-4.2
12
- - gemfiles/Gemfile.rails-4.1
13
- - gemfiles/Gemfile.rails-4.0
14
- - gemfiles/Gemfile.rails-3.2
15
- # - gemfiles/Gemfile.rails-3.1
16
- # - gemfiles/Gemfile.rails-3.0
12
+ env:
13
+ - "RAILS_VERSION=6.0.0"
14
+ - "RAILS_VERSION=5.2.0"
15
+ - "RAILS_VERSION=5.1.0"
16
+ - "RAILS_VERSION=5.0.0"
17
17
  matrix:
18
18
  fast_finish: true
data/CHANGES.md CHANGED
@@ -1,3 +1,25 @@
1
+ # 0.2.1
2
+
3
+ * Error's full_message with translation fixed thanks to [@marcelolx](https://github.com/trailblazer/reform-rails/pull/85)
4
+
5
+ # 0.2.0
6
+
7
+ * Needs Reform >= 2.3.0.
8
+ * make the inclusion of ActiveModel form builder modules optional when using dry-validation. This can be controlled via `config.reform.enable_active_model_builder_methods = true`.
9
+ * delegate `validates_each` method and allow it to be called outside a validation block.
10
+ * add `case_sensitive` option to Reform Uniqueness validation. Defaults to true.
11
+ * fix bug in uniqueness validation where form has different attribute name to column
12
+ * improve handling of persisted records in uniqueness validator
13
+ * remove params.merge! as it's deprecated in rails 5
14
+ * update to support reform 2.3, the new API means that `errors.add` is delegated to ActiveModel::Errors to support rails 5
15
+ * Fix nested form validation (#53)
16
+ * Errors supports symbol and string lookup (PR #77)
17
+ * Implement respond_to that delegates to AMV errors (PR #78)
18
+ * Drop support for activemodel before 5.0
19
+
20
+ # 0.1.8
21
+ * Drop support to mongoid < 4.
22
+
1
23
  # 0.1.7 (0.1.6 Yanked)
2
24
 
3
25
  * Fix a bug where requiring `form/active_model/validations` in a non-Rails environment wouldn't load all necessary files.
@@ -12,7 +34,7 @@
12
34
 
13
35
  # 0.1.3
14
36
 
15
- * Introduce a railtie to load either `ActiveModel::Validations` *or* `Dry::Validations`. This can be controller via `config.reform.validations = :dry`.
37
+ * Introduce a railtie to load either `ActiveModel::Validations` *or* `Dry::Validations`. This can be controlled via `config.reform.validations = :dry`.
16
38
 
17
39
  # 0.1.2
18
40
 
data/Gemfile CHANGED
@@ -1,6 +1,33 @@
1
- source 'https://rubygems.org'
1
+ git_source(:github) do |repo_name|
2
+ repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
3
+ "https://github.com/#{repo_name}.git"
4
+ end
2
5
 
3
- # Specify your gem's dependencies in reform-rails.gemspec
6
+ source "https://rubygems.org"
4
7
  gemspec
5
8
 
6
- gem "reform", path: "../reform"
9
+ gem 'pry-byebug'
10
+ gem "minitest-line"
11
+
12
+ case ENV["GEMS_SOURCE"]
13
+ when "local"
14
+ gem "reform", path: "../reform"
15
+ when "github"
16
+ gem "reform", github: "trailblazer/reform"
17
+ end
18
+
19
+ rails_version = ENV.fetch("RAILS_VERSION", "6.0.0")
20
+
21
+ # bored of wrestling with rails...
22
+
23
+ gem("mongoid", "< 7.0") unless rails_version.include?('6.0')
24
+
25
+
26
+ gem "activerecord", "~> #{rails_version}"
27
+ gem "railties", "~> #{rails_version}"
28
+ if rails_version.include?('6.0')
29
+ gem "sqlite3", "~> 1.4"
30
+ else
31
+ gem "sqlite3", "~> 1.3", "< 1.4"
32
+ end
33
+ puts "Rails version #{rails_version}"
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2015 Nick Sutterer
3
+ Copyright (c) 2015-Covid_19year Nick Sutterer
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -14,7 +14,7 @@ Simply don't include this gem if you don't want to use the conventional Reform/R
14
14
 
15
15
  ## Documentation
16
16
 
17
- The [full documentation](http://trailblazer.to/gems/reform/#reform-rails) can be found on the Trailblazer page.
17
+ The [full documentation](https://trailblazer.to/2.0/gems/reform/rails.html) can be found on the Trailblazer page.
18
18
 
19
19
  ## Installation
20
20
 
@@ -24,7 +24,16 @@ Add this line to your application's Gemfile:
24
24
  gem 'reform-rails'
25
25
  ```
26
26
 
27
- Reform-rails needs Reform >= 2.2.
27
+ Reform-rails needs Reform >= 2.3.
28
+
29
+ ## Contributing
30
+
31
+ By default your tests will run against rails 6.0.1.
32
+ Please ensure that you test your changes against all supported ruby and rails versions (see .travis.yml)
33
+
34
+ You can run tests for a specific version of rails by running the following:
35
+
36
+ `export RAILS_VERSION=5.0.0; bundle update; bundle exec rake test`
28
37
 
29
38
  ## License
30
39
 
data/Rakefile CHANGED
@@ -4,7 +4,9 @@ require "rake/testtask"
4
4
  Rake::TestTask.new(:test) do |t|
5
5
  t.libs << "test"
6
6
  t.libs << "lib"
7
- t.test_files = FileList['test/**/*_test.rb']
7
+ t.test_files = FileList['test/*_test.rb']
8
+ t.warning = false
9
+ t.verbose = true
8
10
  end
9
11
 
10
12
  task :default => :test
@@ -1,4 +1,4 @@
1
- require "reform/form/active_model"
2
1
  require "reform/form/orm"
2
+ require "reform/form/active_model"
3
3
  require "reform/form/active_record"
4
4
  require "reform/form/active_model/model_reflections" # only load this in AR context as simple_form currently is bound to AR.
@@ -42,15 +42,19 @@ module Reform::Form::ActiveModel
42
42
 
43
43
  # moved from reform as not applicable to dry
44
44
  def validates(*args, &block)
45
- validation(:default, inherit: true) { validates *args, &block }
45
+ validation(name: :default, inherit: true) { validates *args, &block }
46
46
  end
47
47
 
48
48
  def validate(*args, &block)
49
- validation(:default, inherit: true) { validate *args, &block }
49
+ validation(name: :default, inherit: true) { validate *args, &block }
50
50
  end
51
51
 
52
52
  def validates_with(*args, &block)
53
- validation(:default, inherit: true) { validates_with *args, &block }
53
+ validation(name: :default, inherit: true) { validates_with *args, &block }
54
+ end
55
+
56
+ def validates_each(*args, &block)
57
+ validation(name: :default, inherit: true) { validates_each *args, &block }
54
58
  end
55
59
 
56
60
  # Set a model name for this form if the infered is wrong.
@@ -85,7 +89,6 @@ module Reform::Form::ActiveModel
85
89
 
86
90
  private
87
91
  def active_model_name_for(string, namespace=nil)
88
- return ::ActiveModel::Name.new(OpenStruct.new(:name => string)) if ::ActiveModel::VERSION::MAJOR == 3 and ::ActiveModel::VERSION::MINOR == 0 # TODO: remove when we drop rails 3.x.
89
92
  ::ActiveModel::Name.new(self, namespace, string)
90
93
  end
91
94
  end # ClassMethods
@@ -2,114 +2,239 @@ require "active_model"
2
2
  require "reform/form/active_model"
3
3
  require "uber/delegates"
4
4
 
5
- module Reform::Form::ActiveModel
5
+ module Reform
6
+ module Form::ActiveModel
6
7
  # AM::Validations for your form.
7
8
  # Provides ::validates, ::validate, #validate, and #valid?.
8
9
  #
9
10
  # Most of this file contains unnecessary wiring to make ActiveModel's error message magic work.
10
11
  # Since Rails still thinks it's a good idea to do things like object.class.human_attribute_name,
11
12
  # we have some hacks in here to provide that. If it doesn't work for you, don't blame us.
12
- module Validations
13
- def self.included(includer)
14
- includer.instance_eval do
15
- include Reform::Form::ActiveModel
16
-
17
- class << self
18
- extend Uber::Delegates
19
- # # Hooray! Delegate translation back to Reform's Validator class which contains AM::Validations.
20
- delegates :active_model_really_sucks, :human_attribute_name, :lookup_ancestors, :i18n_scope # Rails 3.1.
13
+ module Validations
14
+ def self.included(includer)
15
+ includer.instance_eval do
16
+ include Reform::Form::ActiveModel
17
+
18
+ class << self
19
+ extend Uber::Delegates
20
+ # # Hooray! Delegate translation back to Reform's Validator class which contains AM::Validations.
21
+ delegates :active_model_really_sucks, :human_attribute_name, :lookup_ancestors, :i18n_scope # Rails 3.1.
22
+
23
+ def validation_group_class
24
+ Group
25
+ end
21
26
 
22
- def validation_group_class
23
- Group
27
+ # this is to allow calls like Form::human_attribute_name (note that this is on the CLASS level) to be resolved.
28
+ # those calls happen when adding errors in a custom validation method, which is defined on the form (as an instance method).
29
+ def active_model_really_sucks
30
+ Class.new(Validator).tap do |v|
31
+ v.model_name = model_name
32
+ end
33
+ end
24
34
  end
35
+ end # ::included
36
+ end
25
37
 
26
- # this is to allow calls like Form::human_attribute_name (note that this is on the CLASS level) to be resolved.
27
- # those calls happen when adding errors in a custom validation method, which is defined on the form (as an instance method).
28
- def active_model_really_sucks
29
- Class.new(Validator).tap do |v|
30
- v.model_name = model_name
31
- end
38
+ # The concept of "composition" has still not arrived in Rails core and they rely on 400 methods being
39
+ # available in one object. This is why we need to provide parts of the I18N API in the form.
40
+ def read_attribute_for_validation(name)
41
+ send(name)
42
+ end
43
+
44
+ def initialize(*)
45
+ super
46
+ @amv_errors = ActiveModel::Errors.new(self)
47
+ end
48
+
49
+ # Problem with this method is, it's being used in two completely different contexts: Once to add errors in validations,
50
+ # and second to expose errors for presentation.
51
+ def errors(*args)
52
+ @amv_errors
53
+ end
54
+
55
+ def custom_errors
56
+ # required to keep update the ActiveModel::Errors#details used to test for
57
+ # added errors ActiveModel::Errors#added? and needs to be inside this block!
58
+ super.each do |custom_error|
59
+ errors = custom_error.errors
60
+ # CustomError build always the errors with an hash where the value is an array
61
+ errors.values.first.each do |value|
62
+ @amv_errors.add(errors.keys.first, value)
32
63
  end
33
64
  end
34
- end # ::included
35
- end
65
+ end
36
66
 
37
- def build_errors
38
- Errors.new(self)
39
- end
67
+ def validate!(params, pointers=[])
68
+ @amv_errors = ActiveModel::Errors.new(self)
40
69
 
41
- # The concept of "composition" has still not arrived in Rails core and they rely on 400 methods being
42
- # available in one object. This is why we need to provide parts of the I18N API in the form.
43
- def read_attribute_for_validation(name)
44
- send(name)
45
- end
70
+ super.tap do
71
+ # @fran: super ugly hack thanks to the shit architecture of AMV. let's drop it in 3.0 and move on!
72
+ all_errors = @result.to_results
73
+ nested_errors = @result.instance_variable_get(:@failure)
46
74
 
47
- class Group
48
- def initialize(*)
49
- @validations = Class.new(Reform::Form::ActiveModel::Validations::Validator)
75
+ @result = Reform::Contract::Result.new(all_errors, [nested_errors].compact)
76
+
77
+ @amv_errors = Result::ResultErrors.new(@result, self, @result.success?, @amv_errors)
78
+ end
79
+ @result
50
80
  end
51
81
 
52
- extend Uber::Delegates
53
- delegates :@validations, :validates, :validate, :validates_with, :validate_with
82
+ class Group
83
+ def initialize(*)
84
+ @validations = Class.new(Reform::Form::ActiveModel::Validations::Validator)
85
+ end
86
+
87
+ extend Uber::Delegates
88
+ delegates :@validations, :validates, :validate, :validates_with, :validate_with, :validates_each
54
89
 
55
- def call(fields, errors, form) # FIXME.
56
- validator = @validations.new(form)
57
- validator.valid?
90
+ def call(form)
91
+ validator = @validations.new(form)
92
+ validator.instance_variable_set(:@errors, form.errors)
93
+ success = validator.valid? # run the validations.
58
94
 
59
- validator.errors.each do |name, error| # TODO: handle with proper merge, or something. validator.errors is ALWAYS AM::Errors.
60
- errors.add(name, error)
95
+ Result.new(success, validator.errors.messages)
61
96
  end
62
97
  end
63
- end
64
98
 
65
- # Validator is the validatable object. On the class level, we define validations,
66
- # on instance, it exposes #valid?.
67
- require "delegate"
68
- class Validator < SimpleDelegator
69
- # current i18n scope: :activemodel.
70
- include ActiveModel::Validations
99
+ # The idea here to mimic Dry.RB's Result API.
100
+ class Result < Hash # FIXME; should this be AMV::Errors?
101
+ def initialize(success, hash)
102
+ super()
103
+ @success = success
104
+ hash.each { |k,v| self[k] = v }
105
+ end
71
106
 
72
- class << self
73
- def model_name
74
- @_active_model_sucks ||= ActiveModel::Name.new(Reform::Form, nil, "Reform::Form")
107
+ def success?
108
+ @success
75
109
  end
76
110
 
77
- def model_name=(name)
78
- @_active_model_sucks = name
111
+ def failure?
112
+ ! success?
79
113
  end
80
114
 
81
- def validates(*args, &block)
82
- super(*Declarative::DeepDup.(args), &block)
115
+ def messages
116
+ self
83
117
  end
84
118
 
85
- # Prevent AM:V from mutating the validator class
86
- def attr_reader(*)
119
+ # DISCUSS @FRAN: not sure this is 100% compatible with AMV::Errors?
120
+ def errors
121
+ self
87
122
  end
88
123
 
89
- def attr_writer(*)
124
+ class ResultErrors < ::Reform::Contract::Result::Errors # to expose via #errors. i hate it.
125
+ def initialize(a, b, success, amv_errors)
126
+ super(a, b)
127
+ @success = success
128
+ @amv_errors = amv_errors
129
+ end
130
+
131
+ def empty?
132
+ @success
133
+ end
134
+
135
+ def [](k)
136
+ super(k.to_sym) || []
137
+ end
138
+
139
+ # rails expects this to return a stringified hash of the messages
140
+ def to_s
141
+ messages.to_s
142
+ end
143
+
144
+ def add(key, error_text)
145
+ # use rails magic to get the correct error_text and make sure we still update details and fields
146
+ text = @amv_errors.add(key, error_text)
147
+
148
+ # using error_text instead of text to either keep the symbol which will be
149
+ # magically replaced with the translate or directly the string - this is also
150
+ # required otherwise in the custom_errors method we will add the actual message in the
151
+ # ActiveModel::Errors#details which is not correct if a symbol was passed here
152
+ Reform::Contract::CustomError.new(key, error_text, @result.to_results)
153
+
154
+ # but since messages method is actually already defined in `Reform::Contract::Result::Errors
155
+ # we need to update the @dotted_errors instance variable to add or merge a new error
156
+ @dotted_errors.key?(key) ? @dotted_errors[key] |= text : @dotted_errors[key] = text
157
+ instance_variable_set(:@dotted_errors, @dotted_errors)
158
+ end
159
+
160
+ def method_missing(m, *args, &block)
161
+ @amv_errors.send(m, *args, &block) # send all methods to the AMV errors, even privates.
162
+ end
163
+
164
+ def respond_to?(method)
165
+ @amv_errors.respond_to?(method) ? true : super
166
+ end
167
+
168
+ def full_messages
169
+ base_errors = @amv_errors.full_messages
170
+ form_fields = @amv_errors.instance_variable_get(:@base).instance_variable_get(:@fields)
171
+ nested_errors = full_messages_for_nested_fields(form_fields)
172
+
173
+ [base_errors, nested_errors].flatten.compact
174
+ end
175
+
176
+ private
177
+
178
+ def full_messages_for_nested_fields(form_fields)
179
+ form_fields.map { |field| full_messages_for_twin(field[1]) }
180
+ end
181
+
182
+ def full_messages_for_twin(object)
183
+ return get_collection_errors(object) if object.is_a? Disposable::Twin::Collection
184
+ return get_amv_errors(object) if object.is_a? Disposable::Twin
185
+
186
+ nil
187
+ end
188
+
189
+ def get_collection_errors(twin_collection)
190
+ twin_collection.map { |twin| get_amv_errors(twin) }
191
+ end
192
+
193
+ def get_amv_errors(object)
194
+ object.instance_variable_get(:@amv_errors).full_messages
195
+ end
90
196
  end
91
197
  end
92
198
 
93
- def initialize(form)
94
- super(form)
95
- self.class.model_name = form.model_name # one of the many reasons why i will drop support for AM::V in 2.1. or maybe a bit later.
96
- end
199
+ # Validator is the validatable object. On the class level, we define validations,
200
+ # on instance, it exposes #valid?.
201
+ require "delegate"
202
+ class Validator < SimpleDelegator
203
+ # current i18n scope: :activemodel.
204
+ include ActiveModel::Validations
205
+
206
+ class << self
207
+ def model_name
208
+ @_active_model_sucks ||= ActiveModel::Name.new(Reform::Form, nil, "Reform::Form")
209
+ end
210
+
211
+ def model_name=(name)
212
+ @_active_model_sucks = name
213
+ end
214
+
215
+ def validates(*args, &block)
216
+ super(*Declarative::DeepDup.(args), &block)
217
+ end
97
218
 
98
- # FIXME: for some weird reason, Object#format is reserved and form.send(:format) crashes with ArgumentError: too few arguments.
99
- # method_missing fix doesn't work with ruby 2.0 or 2.3
219
+ # Prevent AM:V from mutating the validator class
220
+ def attr_reader(*)
221
+ end
222
+
223
+ def attr_writer(*)
224
+ end
225
+ end
100
226
 
101
- # this fixes 2.0 but I'm guessing that their are more methods than just format to consider
102
- # def format
103
- # __getobj__.format
104
- # end
227
+ def initialize(form)
228
+ super(form)
229
+ self.class.model_name = form.model_name
230
+ end
105
231
 
106
- def method_missing(m, *args, &block)
107
- __getobj__.send(m, *args, &block) # send all methods to the form, even privates.
232
+ def method_missing(m, *args, &block)
233
+ __getobj__.send(m, *args, &block) # send all methods to the form, even privates.
234
+ end
108
235
  end
109
236
  end
110
- end
111
237
 
112
- class Errors < ActiveModel::Errors
113
- include Reform::Form::Errors::Merge
238
+
114
239
  end
115
240
  end