custom_fields 2.12.1 → 2.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +4 -5
  4. data/lib/custom_fields/extensions/active_support.rb +22 -23
  5. data/lib/custom_fields/extensions/carrierwave.rb +9 -40
  6. data/lib/custom_fields/extensions/mongoid/association/referenced/has_many.rb +49 -0
  7. data/lib/custom_fields/extensions/mongoid/association/referenced/has_one.rb +17 -0
  8. data/lib/custom_fields/extensions/mongoid/association/relatable.rb +30 -0
  9. data/lib/custom_fields/extensions/mongoid/criteria/queryable/smash.rb +3 -6
  10. data/lib/custom_fields/extensions/mongoid/document.rb +4 -8
  11. data/lib/custom_fields/extensions/mongoid/factory.rb +7 -9
  12. data/lib/custom_fields/extensions/mongoid/fields/i18n.rb +17 -17
  13. data/lib/custom_fields/extensions/mongoid/fields/localized.rb +21 -21
  14. data/lib/custom_fields/extensions/mongoid/fields.rb +4 -7
  15. data/lib/custom_fields/extensions/mongoid/validatable/collection_size.rb +86 -0
  16. data/lib/custom_fields/extensions/mongoid/{validations → validatable}/macros.rb +3 -2
  17. data/lib/custom_fields/extensions/origin/smash.rb +4 -5
  18. data/lib/custom_fields/field.rb +39 -36
  19. data/lib/custom_fields/source.rb +46 -44
  20. data/lib/custom_fields/target.rb +11 -12
  21. data/lib/custom_fields/target_helpers.rb +21 -23
  22. data/lib/custom_fields/types/belongs_to.rb +6 -20
  23. data/lib/custom_fields/types/boolean.rb +5 -12
  24. data/lib/custom_fields/types/color.rb +4 -12
  25. data/lib/custom_fields/types/date.rb +20 -22
  26. data/lib/custom_fields/types/date_time.rb +16 -18
  27. data/lib/custom_fields/types/default.rb +16 -24
  28. data/lib/custom_fields/types/email.rb +6 -13
  29. data/lib/custom_fields/types/file.rb +26 -35
  30. data/lib/custom_fields/types/float.rb +10 -13
  31. data/lib/custom_fields/types/has_many.rb +12 -16
  32. data/lib/custom_fields/types/integer.rb +10 -13
  33. data/lib/custom_fields/types/json.rb +18 -29
  34. data/lib/custom_fields/types/many_to_many.rb +14 -19
  35. data/lib/custom_fields/types/money.rb +34 -35
  36. data/lib/custom_fields/types/password.rb +19 -23
  37. data/lib/custom_fields/types/relationship_default.rb +4 -16
  38. data/lib/custom_fields/types/select.rb +43 -51
  39. data/lib/custom_fields/types/string.rb +5 -13
  40. data/lib/custom_fields/types/tags.rb +11 -11
  41. data/lib/custom_fields/types/text.rb +4 -16
  42. data/lib/custom_fields/version.rb +4 -2
  43. data/lib/custom_fields.rb +16 -16
  44. metadata +38 -38
  45. data/lib/custom_fields/extensions/mongoid/relations/options.rb +0 -19
  46. data/lib/custom_fields/extensions/mongoid/relations/referenced/in.rb +0 -18
  47. data/lib/custom_fields/extensions/mongoid/relations/referenced/many.rb +0 -30
  48. data/lib/custom_fields/extensions/mongoid/validations/collection_size.rb +0 -43
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3357d5d55b5957c388cd463667d4469225b2b2c5fcd2070c352e24356376d1f0
4
- data.tar.gz: 074afdf57443c539afc384e140f1c0203f57f719207439bc61cee08733d35947
3
+ metadata.gz: 2c5b67a98147c90e024cfcd50690b26ac3d472837222ab577ed4e8cd211f97cf
4
+ data.tar.gz: 4564688d1fec136420b4353f23c8ff149f8ea5240c693a54b863925c5a262373
5
5
  SHA512:
6
- metadata.gz: 3f7b9e7dc8dcb0a187ecbe36a05bfcce3a54911b94687beda218ee2b9c9274726eea8ea66598cf3f88cc5b143890f5d772ab9108a4ce90fbd8defd3ad35c8fb2
7
- data.tar.gz: dc658cae3dd2628c9e43be55405e7a9c935623995e876976ca6ef751096d4a01604e44a29fdf778c7417f4d988d60f0aaf5a469cd2ea3cfa2477c7644b97e37d
6
+ metadata.gz: 1f4226fa0228d6ca2ab6a4b9d35a621560292edfd8baec8f68387954246d95b101d69ca6fd5ddf5a1a4859f24a2270c65b7917f8d71286388a90e9f54e73efc0
7
+ data.tar.gz: 772923aa4070480b8e7170783f75e3c7ef33620a6c85bc9b6dbfa31d65a5458373ef476c1ff84678e21ea272a1fe418de6a282b4bc41ecfcc81bf5333f5e2203
data/MIT-LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2018 NoCoffee
3
+ Copyright (c) 2023 NoCoffee
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy of
6
6
  this software and associated documentation files (the "Software"), to deal in
data/README.md CHANGED
@@ -2,7 +2,6 @@
2
2
  ==============
3
3
 
4
4
  [![Travis CI Status][Travis CI Status]][Travis CI]
5
- [![Gemnasium Status][Gemnasium Status]][Gemnasium]
6
5
 
7
6
  Manage custom fields to a Mongoid document or a collection. This module is one of the core features we implemented in
8
7
  our custom CMS, named LocomotiveCMS. Basically, its aim is to provide to editors a way to manage extra fields to a
@@ -16,9 +15,9 @@ The main goals:
16
15
  Requirements
17
16
  ------------
18
17
 
19
- * MongoDB 3.x
20
- * Mongoid 6.x
21
- * ActiveSupport 5.2.x
18
+ * MongoDB 6+
19
+ * Mongoid 8+
20
+ * ActiveSupport 6+
22
21
 
23
22
  Examples
24
23
  --------
@@ -93,7 +92,7 @@ Feel free to contact me at did at locomotivecms dot com.
93
92
  License
94
93
  -------
95
94
 
96
- Copyright (c) 2018 NoCoffee, released under the [MIT License (MIT)], see [MIT-LICENSE].
95
+ Copyright (c) 2023 NoCoffee, released under the [MIT License (MIT)], see [MIT-LICENSE].
97
96
 
98
97
  [CustomFields]: https://github.com/locomotivecms/custom_fields "Custom fields extension for Mongoid."
99
98
  [Gemnasium]: https://gemnasium.com/locomotivecms/custom_fields "CustomFields at Gemnasium"
@@ -1,30 +1,29 @@
1
- class String
1
+ # frozen_string_literal: true
2
2
 
3
- def constantize_with_custom_fields
4
- begin
5
- constantize_without_custom_fields
6
- rescue NameError => exception
7
- # DEBUG: puts "constantizing #{self.inspect}"
8
- # alright, does it look like a custom_fields dynamic klass ?
9
- if self =~ /(.*)([0-9a-fA-F]{24})$/
10
- base = $1.constantize
11
- # we can know it for sure
12
- if base.with_custom_fields?
13
- relation = base.relations.values.detect { |metadata| metadata[:custom_fields_parent_klass] == true }
3
+ module CustomFieldsStringExtension
4
+ def constantize
5
+ super
6
+ rescue NameError => e
7
+ # DEBUG: puts "constantizing #{self.inspect}"
8
+ # alright, does it look like a custom_fields dynamic klass ?
9
+ if self =~ CustomFields::KLASS_REGEXP
10
+ base = ::Regexp.last_match(1).constantize
11
+ # we can know it for sure
12
+ if base.with_custom_fields?
13
+ relation = base.relations.values.detect do |association|
14
+ association.options[:custom_fields_parent_klass] == true
15
+ end
14
16
 
15
- # load the class which holds the recipe to build the dynamic klass
16
- if relation && parent_instance = relation.klass.find($2)
17
- # DEBUG: puts "re-building #{self}"
18
- return parent_instance.klass_with_custom_fields(relation.inverse_of)
19
- end
17
+ # load the class which holds the recipe to build the dynamic klass
18
+ if relation && parent_instance = relation.klass.find(::Regexp.last_match(2))
19
+ # DEBUG: puts "re-building #{self}"
20
+ return parent_instance.klass_with_custom_fields(relation.inverse_of)
20
21
  end
21
22
  end
22
- # not a custom_fields dynamic klass or unable to re-build it
23
- raise exception
24
23
  end
24
+ # not a custom_fields dynamic klass or unable to re-build it
25
+ raise e
25
26
  end
26
-
27
- alias_method :constantize_without_custom_fields, :constantize
28
- alias_method :constantize, :constantize_with_custom_fields
29
-
30
27
  end
28
+
29
+ ::String.prepend CustomFieldsStringExtension
@@ -1,34 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'carrierwave/mongoid'
2
4
 
3
5
  module CarrierWave
4
-
5
- module Mongoid
6
-
7
- def mount_uploader_with_localization(column, uploader=nil, options={}, &block)
8
- mount_uploader_without_localization(column, uploader, options, &block)
9
-
10
- define_method(:read_uploader) do |name|
11
- # puts "read_uploader #{name} / #{read_attribute(name.to_sym).inspect} / #{::Mongoid::Fields::I18n.locale.inspect}" # DEBUG
12
-
13
- value = read_attribute(name.to_sym)
14
-
15
- unless value.nil?
16
- self.class.fields[name.to_s].demongoize(value)
17
- else
18
- nil
19
- end
20
- end
21
- end
22
-
23
- alias_method :mount_uploader_without_localization, :mount_uploader
24
- alias_method :mount_uploader, :mount_uploader_with_localization
25
-
26
- end
27
-
28
6
  class Mounter
29
-
30
7
  def remove_previous_with_localization(before = nil, after = nil)
31
- _before, _after = before, after
8
+ _before = before
9
+ _after = after
32
10
  locale = ::Mongoid::Fields::I18n.locale.to_s
33
11
 
34
12
  # custom case:
@@ -37,28 +15,19 @@ module CarrierWave
37
15
  # a different name.
38
16
  # We absolutely don't want to erase the file in the default locale
39
17
  if record.class.fields[column.to_s]&.localized? &&
40
- record.changes[column]&.first == '_new_'
18
+ record.changes[column]&.first == '_new_'
41
19
  _before = [nil]
42
20
  end
43
21
 
44
22
  # FIXME: can't reproduce this behavior locally but it happens in production
45
- if before && before.first.is_a?(Hash)
46
- _before = [before.first[locale]]
47
- end
23
+ _before = [before.first[locale]] if before && before.first.is_a?(Hash)
48
24
 
49
- if after && after.first.is_a?(Hash)
50
- _after = [after.first[locale]]
51
- end
25
+ _after = [after.first[locale]] if after && after.first.is_a?(Hash)
52
26
 
53
27
  remove_previous_without_localization(_before, _after)
54
28
  end
55
29
 
56
- alias_method :remove_previous_without_localization, :remove_previous
57
- alias_method :remove_previous, :remove_previous_with_localization
58
-
59
-
30
+ alias remove_previous_without_localization remove_previous
31
+ alias remove_previous remove_previous_with_localization
60
32
  end
61
-
62
33
  end
63
-
64
-
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ # module Mongoid #:nodoc:
4
+ # module Relations #:nodoc:
5
+ # module Referenced #:nodoc:
6
+
7
+ # # This class defines the behaviour for all relations that are a
8
+ # # one-to-many between documents in different collections.
9
+ # class Many < Relations::Many
10
+
11
+ # def build_with_custom_fields(attributes = {}, type = nil)
12
+ # if base.respond_to?(:custom_fields_for?) && base.custom_fields_for?(relation_metadata.name)
13
+ # # all the information about how to build the custom class are stored here
14
+ # recipe = base.custom_fields_recipe_for(relation_metadata.name)
15
+ # attributes ||= {}
16
+ # attributes.merge!(custom_fields_recipe: recipe)
17
+ # # build the class with custom_fields for the first time
18
+ # type = relation_metadata.klass.klass_with_custom_fields(recipe)
19
+ # end
20
+ # build_without_custom_fields(attributes, type)
21
+ # end
22
+
23
+ # alias_method :build_without_custom_fields, :build
24
+ # alias_method :build, :build_with_custom_fields
25
+
26
+ # # new should point to the new build method
27
+ # alias :new :build_with_custom_fields
28
+ # end
29
+
30
+ # end
31
+ # end
32
+ # end
33
+
34
+ module CustomFieldsManyExtension
35
+ def build(attributes = {}, type = nil)
36
+ if _base.respond_to?(:custom_fields_for?) && _base.custom_fields_for?(association.name)
37
+ # all the information about how to build the custom class are stored here
38
+ recipe = _base.custom_fields_recipe_for(association.name)
39
+ attributes ||= {}
40
+ attributes.merge!(custom_fields_recipe: recipe)
41
+ # build the class with custom_fields for the first time
42
+ type = association.klass.klass_with_custom_fields(recipe)
43
+ end
44
+ super(attributes, type)
45
+ end
46
+ alias new build
47
+ end
48
+
49
+ ::Mongoid::Association::Referenced::HasMany::Proxy.prepend CustomFieldsManyExtension
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CustomFieldsInExtension
4
+ module ClassMethods
5
+ def valid_options
6
+ [:custom_fields_parent_klass] + super
7
+ end
8
+ end
9
+
10
+ def self.prepended(base)
11
+ class << base
12
+ prepend ClassMethods
13
+ end
14
+ end
15
+ end
16
+
17
+ ::Mongoid::Association::Referenced::HasOne.prepend CustomFieldsInExtension
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CustomFieldsRelatableExtension
4
+ def resolve_name(mod, name)
5
+ super
6
+ rescue NameError => e
7
+ return name.constantize if name =~ CustomFields::KLASS_REGEXP
8
+
9
+ raise e
10
+ end
11
+
12
+ def validate!
13
+ option = @options.delete(:custom_fields_parent_klass)
14
+ super.tap do
15
+ @options[:custom_fields_parent_klass] = option if option
16
+ end
17
+ end
18
+ end
19
+
20
+ [
21
+ Mongoid::Association::Embedded::EmbeddedIn,
22
+ Mongoid::Association::Embedded::EmbedsMany,
23
+ Mongoid::Association::Embedded::EmbedsOne,
24
+ Mongoid::Association::Referenced::BelongsTo,
25
+ Mongoid::Association::Referenced::HasMany,
26
+ Mongoid::Association::Referenced::HasAndBelongsToMany,
27
+ Mongoid::Association::Referenced::HasOne,
28
+ ].each do |klass|
29
+ klass.prepend CustomFieldsRelatableExtension
30
+ end
@@ -1,11 +1,10 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
+
2
3
  module Mongoid
3
4
  class Criteria
4
5
  module Queryable
5
-
6
6
  # This is a smart hash for use with options and selectors.
7
7
  class Smash < Hash
8
-
9
8
  private
10
9
 
11
10
  # Get the localized value for the key if needed. If the field uses
@@ -24,11 +23,9 @@ module Mongoid
24
23
  #
25
24
  # @since 1.0.0
26
25
  def localized_key(name, serializer)
27
- serializer && serializer.localized? ? "#{name}.#{::Mongoid::Fields::I18n.locale}" : name
26
+ serializer&.localized? ? "#{name}.#{::Mongoid::Fields::I18n.locale}" : name
28
27
  end
29
-
30
28
  end
31
-
32
29
  end
33
30
  end
34
31
  end
@@ -1,9 +1,8 @@
1
- module Mongoid #:nodoc:
2
-
3
- module Document #:nodoc:
4
-
5
- module ClassMethods #:nodoc:
1
+ # frozen_string_literal: true
6
2
 
3
+ module Mongoid # :nodoc:
4
+ module Document # :nodoc:
5
+ module ClassMethods # :nodoc:
7
6
  # The mongoid default document returns always false.
8
7
  # The documents with custom fields return true.
9
8
  #
@@ -12,9 +11,6 @@ module Mongoid #:nodoc:
12
11
  def with_custom_fields?
13
12
  false
14
13
  end
15
-
16
14
  end
17
-
18
15
  end
19
-
20
16
  end
@@ -1,18 +1,16 @@
1
- module Mongoid #:nodoc:
1
+ # frozen_string_literal: true
2
2
 
3
+ module Mongoid # :nodoc:
3
4
  # Instantiates documents that came from the database.
4
5
  module Factory
6
+ def from_db_with_custom_fields(klass, attributes = nil, criteria = nil, selected_fields = nil)
7
+ klass.klass_with_custom_fields(attributes['custom_fields_recipe']) if klass.with_custom_fields?
5
8
 
6
- def from_db_with_custom_fields(klass, attributes = nil, selected_fields = nil)
7
- if klass.with_custom_fields?
8
- klass.klass_with_custom_fields(attributes['custom_fields_recipe'])
9
- end
10
-
11
- from_db_without_custom_fields(klass, attributes, selected_fields)
9
+ from_db_without_custom_fields(klass, attributes, criteria, selected_fields)
12
10
  end
13
11
 
14
12
  # equivalent for "alias_method_chain :from_db, :custom_fields"
15
- alias_method :from_db_without_custom_fields, :from_db unless method_defined?(:from_db_without_custom_fields)
16
- alias_method :from_db, :from_db_with_custom_fields
13
+ alias from_db_without_custom_fields from_db unless method_defined?(:from_db_without_custom_fields)
14
+ alias from_db from_db_with_custom_fields
17
15
  end
18
16
  end
@@ -1,10 +1,11 @@
1
- module Mongoid #:nodoc
1
+ # frozen_string_literal: true
2
2
 
3
+ # :nodoc
4
+
5
+ module Mongoid
3
6
  # This module defines behaviour for fields.
4
7
  module Fields
5
-
6
8
  class I18n
7
-
8
9
  attr_accessor :locale, :fallbacks
9
10
 
10
11
  def self.instance
@@ -12,48 +13,47 @@ module Mongoid #:nodoc
12
13
  end
13
14
 
14
15
  def self.locale
15
- self.instance.locale || ::I18n.locale
16
+ instance.locale || ::I18n.locale
16
17
  end
17
18
 
18
19
  def self.locale=(value)
19
- self.instance.locale = value.to_sym rescue nil
20
+ instance.locale = begin
21
+ value.to_sym
22
+ rescue StandardError
23
+ nil
24
+ end
20
25
  end
21
26
 
22
27
  def self.fallbacks
23
- if !self.instance.fallbacks.blank?
24
- self.instance.fallbacks
28
+ if !instance.fallbacks.blank?
29
+ instance.fallbacks
25
30
  elsif ::I18n.respond_to?(:fallbacks)
26
31
  ::I18n.fallbacks
27
- else
28
- nil
29
32
  end
30
33
  end
31
34
 
32
35
  def self.fallbacks_for(locale, fallbacks)
33
- self.instance.fallbacks ||= {}
34
- self.instance.fallbacks[locale.to_sym] = fallbacks
36
+ instance.fallbacks ||= {}
37
+ instance.fallbacks[locale.to_sym] = fallbacks
35
38
  end
36
39
 
37
40
  def self.fallbacks?
38
- !self.instance.fallbacks.blank? || (::I18n.respond_to?(:fallbacks) && !::I18n.fallbacks.blank?)
41
+ !instance.fallbacks.blank? || (::I18n.respond_to?(:fallbacks) && !::I18n.fallbacks.blank?)
39
42
  end
40
43
 
41
44
  def self.clear_fallbacks
42
- self.instance.fallbacks.try(:clear)
45
+ instance.fallbacks.try(:clear)
43
46
  end
44
47
 
45
48
  def self.with_locale(new_locale = nil)
46
49
  if new_locale
47
- current_locale = self.locale
50
+ current_locale = locale
48
51
  self.locale = new_locale
49
52
  end
50
53
  yield
51
54
  ensure
52
55
  self.locale = current_locale if new_locale
53
56
  end
54
-
55
57
  end
56
-
57
58
  end
58
-
59
59
  end
@@ -1,6 +1,7 @@
1
- module Mongoid #:nodoc:
2
- module Fields #:nodoc:
1
+ # frozen_string_literal: true
3
2
 
3
+ module Mongoid # :nodoc:
4
+ module Fields # :nodoc:
4
5
  # The behaviour of the Localized fields in the custom fields gem is different
5
6
  # because we do not rely on I18n directly but on a slight version Mongoid::Fields::I18n.
6
7
  # The main reason is only practical to handle the following case:
@@ -9,7 +10,6 @@ module Mongoid #:nodoc:
9
10
  # TODO: use this gem instead https://github.com/simi/mongoid-localizer
10
11
  #
11
12
  class Localized < Standard
12
-
13
13
  def mongoize(object)
14
14
  { locale.to_s => type.mongoize(object) }
15
15
  end
@@ -17,33 +17,33 @@ module Mongoid #:nodoc:
17
17
  private
18
18
 
19
19
  def lookup(object)
20
- if !object.respond_to?(:keys) # if no translation hash is given, we return the object itself
21
- object
22
- elsif object.has_key?(locale.to_s)
23
- object[locale.to_s]
24
- elsif I18n.fallbacks?
25
- lookup_with_fallback(object)
26
- else
27
- nil
28
- end
29
- end
20
+ value = if object.key?(locale.to_s)
21
+ object[locale.to_s]
22
+ elsif object.key?(locale)
23
+ object[locale]
24
+ end
25
+ return value unless value.nil?
30
26
 
31
- def lookup_with_fallback(object)
32
- fallback_locales = I18n.fallbacks[locale].try(:map, &:to_s)
27
+ return unless fallbacks? && i18n.respond_to?(:fallbacks)
33
28
 
34
- if fallback_locales
35
- _locale = fallback_locales.find { |loc| !object[loc].nil? }
36
- object[_locale]
37
- else
38
- nil
29
+ fallback_key = i18n.fallbacks[locale].find do |loc|
30
+ object.key?(loc.to_s) || object.key?(loc)
39
31
  end
32
+ object[fallback_key.to_s] || object[fallback_key]
33
+ end
34
+
35
+ def fallbacks?
36
+ i18n.fallbacks?
40
37
  end
41
38
 
42
39
  def locale
43
40
  # be careful, it does not return ::I18n.locale
44
- I18n.locale
41
+ i18n.locale
45
42
  end
46
43
 
44
+ def i18n
45
+ ::Mongoid::Fields::I18n
46
+ end
47
47
  end
48
48
  end
49
49
  end
@@ -1,10 +1,11 @@
1
- module Mongoid #:nodoc
1
+ # frozen_string_literal: true
2
2
 
3
+ # :nodoc
4
+
5
+ module Mongoid
3
6
  # This module defines behaviour for fields.
4
7
  module Fields
5
-
6
8
  module ClassMethods
7
-
8
9
  # Replace a field with a new type.
9
10
  #
10
11
  # @example Replace the field.
@@ -19,13 +20,9 @@ module Mongoid #:nodoc
19
20
  # @since 2.1.0
20
21
  def replace_field(name, type, localize = false)
21
22
  # puts "fields[#{name}] = #{fields[name.to_s].inspect} / #{fields.keys.inspect}" # DEBUG
22
- #attribute_names.delete_one(name)
23
23
  remove_defaults(name)
24
24
  add_field(name, fields[name.to_s].options.merge(type: type, localize: localize))
25
25
  end
26
-
27
26
  end
28
-
29
27
  end
30
-
31
28
  end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mongoid
4
+ module Validatable
5
+ # Validates that the specified collections do or do not match a certain
6
+ # size.
7
+ #
8
+ # @example Set up the collection size validator.
9
+ #
10
+ # class Person
11
+ # include Mongoid::Document
12
+ # has_many :addresses
13
+ #
14
+ # validates_collection_size_of :addresses, in: 1..10
15
+ # end
16
+ class CollectionSizeValidator < Mongoid::Validatable::LengthValidator
17
+ def validate_each(record, attribute, value)
18
+ value = collection_to_size(record, attribute)
19
+
20
+ super(record, attribute, value)
21
+ end
22
+
23
+ private
24
+
25
+ def collection_to_size(record, attribute)
26
+ # TODO: find an example with a has_and_belongs_to_many relationship...
27
+ # relation = record.relations[attribute.to_s]
28
+
29
+ # source = case relation.macro
30
+ # when :embeds_many, :has_many
31
+ # record.send(attribute)
32
+ # when :has_and_belongs_to_many
33
+ # record.send(relation.key.to_sym)
34
+ # end
35
+
36
+ source = record.send(attribute)
37
+
38
+ OpenStruct.new(length: source.try(:size) || 0)
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ # module Mongoid
45
+ # module Validations
46
+
47
+ # # Validates that the specified collections do or do not match a certain
48
+ # # size.
49
+ # #
50
+ # # @example Set up the collection size validator.
51
+ # #
52
+ # # class Person
53
+ # # include Mongoid::Document
54
+ # # has_many :addresses
55
+ # #
56
+ # # validates_collection_size_of :addresses, in: 1..10
57
+ # # end
58
+ # class CollectionSizeValidator < Mongoid::Validatable::LengthValidator
59
+
60
+ # def validate_each_with_collection(record, attribute, value)
61
+ # value = collection_to_size(record, attribute)
62
+
63
+ # self.validate_each_without_collection(record, attribute, value)
64
+ # end
65
+
66
+ # alias_method :validate_each_without_collection, :validate_each
67
+ # alias_method :validate_each, :validate_each_with_collection
68
+
69
+ # private
70
+
71
+ # def collection_to_size(record, attribute)
72
+ # relation = record.relations[attribute.to_s]
73
+
74
+ # source = case relation.macro
75
+ # when :embeds_many, :has_many
76
+ # record.send(attribute)
77
+ # when :has_and_belongs_to_many
78
+ # record.send(relation.key.to_sym)
79
+ # end
80
+
81
+ # OpenStruct.new(length: source.try(:size) || 0)
82
+ # end
83
+
84
+ # end
85
+ # end
86
+ # end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Mongoid
2
4
  module Validatable
3
5
  module Macros
@@ -17,9 +19,8 @@ module Mongoid
17
19
  #
18
20
  # @since 2.4.0
19
21
  def validates_collection_size_of(*args)
20
- validates_with(Mongoid::Validations::CollectionSizeValidator, _merge_attributes(args))
22
+ validates_with(Mongoid::Validatable::CollectionSizeValidator, _merge_attributes(args))
21
23
  end
22
-
23
24
  end
24
25
  end
25
26
  end
@@ -1,8 +1,8 @@
1
- module Origin
1
+ # frozen_string_literal: true
2
2
 
3
+ module Origin
3
4
  # This is a smart hash for use with options and selectors.
4
5
  class Smash < Hash
5
-
6
6
  private
7
7
 
8
8
  # Get the normalized value for the key. If localization is in play the
@@ -25,8 +25,7 @@ module Origin
25
25
  # @since 1.0.0
26
26
  def normalized_key(name, serializer)
27
27
  # serializer && serializer.localized? ? "#{name}.#{::I18n.locale}" : name
28
- serializer && serializer.localized? ? "#{name}.#{::Mongoid::Fields::I18n.locale}" : name
28
+ serializer&.localized? ? "#{name}.#{::Mongoid::Fields::I18n.locale}" : name
29
29
  end
30
-
31
30
  end
32
- end
31
+ end