reform-rails 0.1.7 → 0.2.1

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.
@@ -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