custom_fields 2.12.1 → 2.13.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 (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