From 6ed2f3f018f14fda5e34abd2d5843e42d1008c10 Mon Sep 17 00:00:00 2001 From: PageHey Date: Sun, 27 Jul 2025 11:13:37 +0200 Subject: [PATCH 01/10] feat: add ability to configure gem behavior It is used here to toggle popover options validations or insertion in the HTML as attributes of the tag. --- app/models/coupdoeil/popover/options_set.rb | 2 +- app/models/coupdoeil/tag.rb | 2 +- lib/coupdoeil.rb | 6 ++++ lib/coupdoeil/config.rb | 34 +++++++++++++++++++++ lib/coupdoeil/engine.rb | 2 +- test/dummy/config/initializers/coupdoeil.rb | 5 +++ 6 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 lib/coupdoeil/config.rb create mode 100644 test/dummy/config/initializers/coupdoeil.rb diff --git a/app/models/coupdoeil/popover/options_set.rb b/app/models/coupdoeil/popover/options_set.rb index 7388c10..cf2b0e9 100644 --- a/app/models/coupdoeil/popover/options_set.rb +++ b/app/models/coupdoeil/popover/options_set.rb @@ -32,7 +32,7 @@ module Coupdoeil OptionsSet.new(options.merge(options_set.options)) end - if Rails.env.local? + if Coupdoeil.config.validate_options def validate! ORDERED_OPTIONS.map do |option| next unless options.key?(option.key) diff --git a/app/models/coupdoeil/tag.rb b/app/models/coupdoeil/tag.rb index 2a25fcb..fcda0f4 100644 --- a/app/models/coupdoeil/tag.rb +++ b/app/models/coupdoeil/tag.rb @@ -36,7 +36,7 @@ module Coupdoeil attributes.merge!("popover-type" => popover_setup.identifier, "popover-params" => params) end - if Rails.env.local? + if Coupdoeil.config.options_html_attributes attributes.merge!(popover_options.to_h.transform_keys { "popover-#{_1}" }) end diff --git a/lib/coupdoeil.rb b/lib/coupdoeil.rb index 3164efd..d8ad053 100644 --- a/lib/coupdoeil.rb +++ b/lib/coupdoeil.rb @@ -1,8 +1,14 @@ # frozen_string_literal: true require "coupdoeil/version" +require "coupdoeil/config" require "coupdoeil/engine" module Coupdoeil extend ActiveSupport::Autoload + + class << self + def config = Coupdoeil::Config.current + def configure = yield(Coupdoeil::Config.current) + end end diff --git a/lib/coupdoeil/config.rb b/lib/coupdoeil/config.rb new file mode 100644 index 0000000..936fea0 --- /dev/null +++ b/lib/coupdoeil/config.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Coupdoeil + class Config + class << self + def defaults + ActiveSupport::OrderedOptions.new.merge!({ + validate_options: Rails.env.local?, + options_html_attributes: Rails.env.local?, + }) + end + + # @!attribute validate_options + # @return [Boolean] + # Whether to validate popover options when building it. + # It will raise an error with hint if an option value is not valid + # Defaults to `Rails.env.local?`. + + # @!attribute options_html_attributes + # @return [Boolean] + # Whether to insert options as HTML attributes of the tag. + # This is not required for Coupdoeil to work since options are compressed to base 36 to lighten the HTML payload. + # This option is here to help for debug or testing. + # Defaults to `Rails.env.local?`. + end + # @!attribute current + # @return [Coupdoeil::Config] + # Returns the current Coupdoeil::Config. This is persisted against this + # class so that config options remain accessible before the rest of + # Coupdoeil has loaded. Defaults to an instance of Coupdoeil::Config + # with all other documented defaults set. + class_attribute :current, default: defaults, instance_predicate: false + end +end diff --git a/lib/coupdoeil/engine.rb b/lib/coupdoeil/engine.rb index 0a714f4..5a7739d 100644 --- a/lib/coupdoeil/engine.rb +++ b/lib/coupdoeil/engine.rb @@ -4,7 +4,7 @@ module Coupdoeil class Engine < ::Rails::Engine isolate_namespace Coupdoeil config.eager_load_namespaces << Coupdoeil - config.coupdoeil = ActiveSupport::OrderedOptions.new + config.coupdoeil = Coupdoeil::Config.current config.autoload_once_paths = [ "#{root}/app/controllers", diff --git a/test/dummy/config/initializers/coupdoeil.rb b/test/dummy/config/initializers/coupdoeil.rb new file mode 100644 index 0000000..86d06c4 --- /dev/null +++ b/test/dummy/config/initializers/coupdoeil.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +Coupdoeil.configure do |config| + config.options_html_attributes = false +end -- GitLab From a954002d928ba4e680eeac99d51880d4d00e2a8f Mon Sep 17 00:00:00 2001 From: PageHey Date: Sun, 27 Jul 2025 12:52:16 +0200 Subject: [PATCH 02/10] Fix config class loading --- lib/coupdoeil.rb | 1 - lib/coupdoeil/engine.rb | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/coupdoeil.rb b/lib/coupdoeil.rb index d8ad053..e56e8c9 100644 --- a/lib/coupdoeil.rb +++ b/lib/coupdoeil.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "coupdoeil/version" -require "coupdoeil/config" require "coupdoeil/engine" module Coupdoeil diff --git a/lib/coupdoeil/engine.rb b/lib/coupdoeil/engine.rb index 5a7739d..fb3f962 100644 --- a/lib/coupdoeil/engine.rb +++ b/lib/coupdoeil/engine.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "coupdoeil/config" + module Coupdoeil class Engine < ::Rails::Engine isolate_namespace Coupdoeil -- GitLab From df04da20c5ea807cb3fb97525375b42abfc1a0d5 Mon Sep 17 00:00:00 2001 From: PageHey Date: Sun, 27 Jul 2025 12:52:32 +0200 Subject: [PATCH 03/10] Use guard clause instead of conditional method definition for validation --- app/models/coupdoeil/popover/options_set.rb | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/app/models/coupdoeil/popover/options_set.rb b/app/models/coupdoeil/popover/options_set.rb index cf2b0e9..1a1cc65 100644 --- a/app/models/coupdoeil/popover/options_set.rb +++ b/app/models/coupdoeil/popover/options_set.rb @@ -32,17 +32,15 @@ module Coupdoeil OptionsSet.new(options.merge(options_set.options)) end - if Coupdoeil.config.validate_options - def validate! - ORDERED_OPTIONS.map do |option| - next unless options.key?(option.key) - - value = options[option.key] - option.new(value).validate! - end + def validate! + return if Coupdoeil.config.validate_options == false || @to_base36.present? + + ORDERED_OPTIONS.map do |option| + next unless options.key?(option.key) + + value = options[option.key] + option.new(value).validate! end - else - def validate! = nil # no-op end def to_base36 -- GitLab From 9212a6314d158d83e856db3ae2679c25d5f2ae08 Mon Sep 17 00:00:00 2001 From: PageHey Date: Sun, 27 Jul 2025 12:52:56 +0200 Subject: [PATCH 04/10] Update documentation about configuration --- docs/guides/authentication.md | 2 +- docs/guides/configuration.md | 49 +++++++++++++++++++++++++++++++++++ docs/html-elements.md | 6 ++--- docs/options.md | 11 ++++---- lib/coupdoeil/config.rb | 2 +- 5 files changed, 59 insertions(+), 11 deletions(-) create mode 100644 docs/guides/configuration.md diff --git a/docs/guides/authentication.md b/docs/guides/authentication.md index 7f73b4a..c08c26c 100644 --- a/docs/guides/authentication.md +++ b/docs/guides/authentication.md @@ -2,7 +2,7 @@ layout: default title: Handle authentication parent: How-to guides -nav_order: 6 +nav_order: 7 --- # {{ page.title }} diff --git a/docs/guides/configuration.md b/docs/guides/configuration.md new file mode 100644 index 0000000..cb62a9e --- /dev/null +++ b/docs/guides/configuration.md @@ -0,0 +1,49 @@ +--- +layout: default +title: Configuration +parent: How-to guides +nav_order: 6 +--- + +# {{ page.title }} + +To configure Coupdoeil you can either use an initializer: + +```ruby +# config/initializers/coupdoeil.rb + +Coupdoeil.configure do |config| + config.validate_options = true +end +``` + +or set options in `config/environments/ENVIRONMENT.rb`: + +```ruby +# config/environments/production.rb + +MyApplication.configure do + config.coupdoeil.validate_options = false +end +``` + +### `validate_options` + +| Type | **Boolean** | +| Default | `Rails.env.local?` | + +Whether to validate popover options when building the tag when calling `coupdoeil_popover_tag`. +It will raise an error with a hint on how to fix it if an option's value is not valid. + +This is enabled in local envs only to save some resources in production, especially on pages rendering a lot a `` tags (not a huge amount, but nothing's negligible if traffic is high or cumulated over a long period of time). + +Note that default options are validated and compressed once since their compressed version is memoized. So, if you never pass custom option to the helper this option won't have much impact. + +### `attribute options_html_attributes` + +| Type | **Boolean** | +| Default | `Rails.env.local?` | + +Whether to insert options as HTML attributes of the `` tag. +This is not required for Coupdoeil to work since options are compressed as base 36 to lighten the HTML payload. +This option is here to help for debug or testing. diff --git a/docs/html-elements.md b/docs/html-elements.md index e821e14..1fb3954 100644 --- a/docs/html-elements.md +++ b/docs/html-elements.md @@ -16,16 +16,14 @@ If you inspect the DOM where there is a popover set up you'll see it: ``` ```html -

My Project

``` -When inspecting element in local environment (dev, test), you can also see all the options. -But to avoid cluttering the DOM with very repetitive data, and overburden HTML payload, in production the options are compressed to the minimum. -This is the `popover-options="c"` attribute that can be seen in the example above. +The `popover-options="63r41l"` attribute that can be seen in the example above are the options compressed to lighten the HTML payload. By default, when inspecting element in local environments, you can also see all the options as attributes of the element, but not in production ([this is configurable](/guides/configuration.html#attribute-options_html_attributes)). If you're interested to know how this compression works, you can take a look at `#to_base36` method in `Coupdoeil::Popover::OptionsSet`. When the popover is open, the `coup-doeil` tag attribute `data-popover-open` is set to `"true"`. diff --git a/docs/options.md b/docs/options.md index ae577f4..fc627e1 100644 --- a/docs/options.md +++ b/docs/options.md @@ -19,14 +19,15 @@ This facilitates uniform rendering of popovers across your application without h Options are passed as a hash with lower snake cased keys to either [`default_options`](/api.html#default_options---coupdoeilpopoveroptionsset) or [`default_options_for`](/api.html#default_options_for---coupdoeilpopoveroptionsset) methods. -## Differences between development and production -Options are validated when in local environment (development or test). +## Validations and HTML attributes + +By default, options are validated only in local environments (development or test). If an option is not set with a correct value, an error is raised with a hint on how to resolve it. +By default, these validations are skipped in production to speed up the coupdoeil tag rendering. -In production, these validations are skipped to speed up the coupdoeil tag rendering. +Also note that by default, the options are not set on the `` element in production, as they are compressed to the minimum possible size. It is therefore advised not to have any logic relying on the ability to read the options on this element, these are only meant for debug and testing. -Also note that in production, the options are not readable on the `` element, as they are compressed to the minimum possible size. -**You should therefore not have any logic relying on the ability to read the options on this element, these are only available for debug**. +These two behaviors are configurable: [see configuration](/guides/configuration.html). ## Default options for the class diff --git a/lib/coupdoeil/config.rb b/lib/coupdoeil/config.rb index 936fea0..fde6a84 100644 --- a/lib/coupdoeil/config.rb +++ b/lib/coupdoeil/config.rb @@ -13,7 +13,7 @@ module Coupdoeil # @!attribute validate_options # @return [Boolean] # Whether to validate popover options when building it. - # It will raise an error with hint if an option value is not valid + # It will raise an error with a hint on how to fix it if an option's value is not valid. # Defaults to `Rails.env.local?`. # @!attribute options_html_attributes -- GitLab From 1f1dda8c7872ab1d6bd6a7de80746a1e8018a227 Mon Sep 17 00:00:00 2001 From: PageHey Date: Sun, 27 Jul 2025 12:55:34 +0200 Subject: [PATCH 05/10] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab98f04..dd947ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### next version - refactor: remove unused logger +- feat: add ability to configure gem behavior ### v1.0.3 - fix commit f989786 that changed the wrong line (0b7832e) -- GitLab From 4fc26e055d277a6b7c9dcb03c11086b09e9414a2 Mon Sep 17 00:00:00 2001 From: PageHey Date: Sun, 27 Jul 2025 14:00:33 +0200 Subject: [PATCH 06/10] Improve config documentation --- docs/guides/configuration.md | 18 ++++++++++-------- docs/html-elements.md | 4 ++-- docs/options.md | 22 +++++++++++++++++----- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/docs/guides/configuration.md b/docs/guides/configuration.md index cb62a9e..23beec2 100644 --- a/docs/guides/configuration.md +++ b/docs/guides/configuration.md @@ -17,7 +17,7 @@ Coupdoeil.configure do |config| end ``` -or set options in `config/environments/ENVIRONMENT.rb`: +or set it up in `config/environments/ENVIRONMENT.rb`: ```ruby # config/environments/production.rb @@ -27,23 +27,25 @@ MyApplication.configure do end ``` -### `validate_options` +## Config attributes + +### `.validate_options` | Type | **Boolean** | | Default | `Rails.env.local?` | -Whether to validate popover options when building the tag when calling `coupdoeil_popover_tag`. +Whether to validate popover options before building the tag when calling `coupdoeil_popover_tag`. It will raise an error with a hint on how to fix it if an option's value is not valid. -This is enabled in local envs only to save some resources in production, especially on pages rendering a lot a `` tags (not a huge amount, but nothing's negligible if traffic is high or cumulated over a long period of time). +This is enabled in local envs only to save some resources in production, especially on pages rendering a lot a `` tags (not a huge save, but nothing's negligible if traffic is high or cumulated over a long period of time). Note that default options are validated and compressed once since their compressed version is memoized. So, if you never pass custom option to the helper this option won't have much impact. -### `attribute options_html_attributes` +### `.options_html_attributes` | Type | **Boolean** | | Default | `Rails.env.local?` | -Whether to insert options as HTML attributes of the `` tag. -This is not required for Coupdoeil to work since options are compressed as base 36 to lighten the HTML payload. -This option is here to help for debug or testing. +Whether to insert options as HTML attributes of the `` tag for inspection. +This is not required for popovers to work since options are compressed as base 36 to lighten the HTML payload. +This option is meant to help for debug or testing. diff --git a/docs/html-elements.md b/docs/html-elements.md index 1fb3954..b5d26cc 100644 --- a/docs/html-elements.md +++ b/docs/html-elements.md @@ -23,7 +23,7 @@ If you inspect the DOM where there is a popover set up you'll see it: ``` -The `popover-options="63r41l"` attribute that can be seen in the example above are the options compressed to lighten the HTML payload. By default, when inspecting element in local environments, you can also see all the options as attributes of the element, but not in production ([this is configurable](/guides/configuration.html#attribute-options_html_attributes)). +The `popover-options="63r41l"` attribute that can be seen in the example above are the options compressed to lighten the HTML payload. By default, when inspecting element in local environments you can also see all the options as attributes of the element. This is not the case in production but [this is configurable](/guides/configuration.html#attribute-options_html_attributes). If you're interested to know how this compression works, you can take a look at `#to_base36` method in `Coupdoeil::Popover::OptionsSet`. When the popover is open, the `coup-doeil` tag attribute `data-popover-open` is set to `"true"`. @@ -35,7 +35,7 @@ When rendering a popover, you can think of it made of three layers: 2. the layout 3. the content -You handle the two former elements, but the first one is fully handled by the library. +You handle the two former elements, and the first one is fully handled by the library. The positioned element has the `.coupdoeil--popover` CSS class, a `data-placement` attribute that is set to the current popover position relative to its target, diff --git a/docs/options.md b/docs/options.md index fc627e1..d974cc9 100644 --- a/docs/options.md +++ b/docs/options.md @@ -19,15 +19,14 @@ This facilitates uniform rendering of popovers across your application without h Options are passed as a hash with lower snake cased keys to either [`default_options`](/api.html#default_options---coupdoeilpopoveroptionsset) or [`default_options_for`](/api.html#default_options_for---coupdoeilpopoveroptionsset) methods. -## Validations and HTML attributes +## Validations -By default, options are validated only in local environments (development or test). +Options are checked when inserting a popover in a template to ensure they are valid. If an option is not set with a correct value, an error is raised with a hint on how to resolve it. -By default, these validations are skipped in production to speed up the coupdoeil tag rendering. -Also note that by default, the options are not set on the `` element in production, as they are compressed to the minimum possible size. It is therefore advised not to have any logic relying on the ability to read the options on this element, these are only meant for debug and testing. +By default, validations are run in local environments (development or test), and skipped in production to speed up the rendering. -These two behaviors are configurable: [see configuration](/guides/configuration.html). +This behavior is configurable: [validation configuration](/guides/configuration.html#validate_options). ## Default options for the class @@ -97,3 +96,16 @@ ContactPopover.default_options ContactPopover.default_options_for(:tooltip) #=> { animation: false, ... } ``` + +You can also inspect options in the HTML. + +```html + + + +``` + +Note that by default, the options are not set on the `` element in production, as they are compressed to the minimum possible size (and set as the `popover-options` attribute). It is therefore advised not to have any logic relying on the ability to read the options on this element, these attributes are only meant for debug and testing. + +This behavior is configurable: [options HTML attributes configuration](/guides/configuration.html#options_html_attributes). -- GitLab From 34c554a87c42acc6fd03b1715b9e61f50e0737ef Mon Sep 17 00:00:00 2001 From: PageHey Date: Sun, 27 Jul 2025 14:03:08 +0200 Subject: [PATCH 07/10] Improve config documentation --- docs/guides/configuration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guides/configuration.md b/docs/guides/configuration.md index 23beec2..a770798 100644 --- a/docs/guides/configuration.md +++ b/docs/guides/configuration.md @@ -37,9 +37,9 @@ end Whether to validate popover options before building the tag when calling `coupdoeil_popover_tag`. It will raise an error with a hint on how to fix it if an option's value is not valid. -This is enabled in local envs only to save some resources in production, especially on pages rendering a lot a `` tags (not a huge save, but nothing's negligible if traffic is high or cumulated over a long period of time). +This is enabled in local envs only to save some resources in production, especially on pages rendering a lot a `` tags (not a huge save, but nothing's negligible if traffic is high or if we consider resources usage cumulated over a long period of time). -Note that default options are validated and compressed once since their compressed version is memoized. So, if you never pass custom option to the helper this option won't have much impact. +Note that default options are validated only once since their compressed version is memoized. So, if you never pass custom option to the helper this option won't have much impact. ### `.options_html_attributes` -- GitLab From f2c051e687bbc8efa1c9979634f401870e349295 Mon Sep 17 00:00:00 2001 From: PageHey Date: Sun, 27 Jul 2025 14:06:34 +0200 Subject: [PATCH 08/10] Improve config documentation --- docs/html-elements.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/html-elements.md b/docs/html-elements.md index b5d26cc..da3a007 100644 --- a/docs/html-elements.md +++ b/docs/html-elements.md @@ -23,8 +23,7 @@ If you inspect the DOM where there is a popover set up you'll see it: ``` -The `popover-options="63r41l"` attribute that can be seen in the example above are the options compressed to lighten the HTML payload. By default, when inspecting element in local environments you can also see all the options as attributes of the element. This is not the case in production but [this is configurable](/guides/configuration.html#attribute-options_html_attributes). -If you're interested to know how this compression works, you can take a look at `#to_base36` method in `Coupdoeil::Popover::OptionsSet`. +The `popover-options="63r41l"` attribute that can be seen in the example above are the popover options compressed to lighten the HTML payload. If you're interested to know how this compression works, you can take a look at `#to_base36` method in `Coupdoeil::Popover::OptionsSet`. See [inspecting options](/options.html#inspecting-options) for debug or testing. When the popover is open, the `coup-doeil` tag attribute `data-popover-open` is set to `"true"`. -- GitLab From 72d7292a2f1de8ee2d414e46bfe10cd40735620c Mon Sep 17 00:00:00 2001 From: PageHey Date: Sun, 27 Jul 2025 14:10:53 +0200 Subject: [PATCH 09/10] Improve config documentation --- docs/options.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/options.md b/docs/options.md index d974cc9..4cc6b58 100644 --- a/docs/options.md +++ b/docs/options.md @@ -21,7 +21,7 @@ Options are passed as a hash with lower snake cased keys to either [`default_opt ## Validations -Options are checked when inserting a popover in a template to ensure they are valid. +Options are validated when inserting a popover in a template. If an option is not set with a correct value, an error is raised with a hint on how to resolve it. By default, validations are run in local environments (development or test), and skipped in production to speed up the rendering. -- GitLab From 356b22b9742954bb738b908b893aa4ae5b939938 Mon Sep 17 00:00:00 2001 From: PageHey Date: Sun, 27 Jul 2025 22:34:21 +0200 Subject: [PATCH 10/10] Improve config documentation --- docs/html-elements.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/html-elements.md b/docs/html-elements.md index da3a007..a25be29 100644 --- a/docs/html-elements.md +++ b/docs/html-elements.md @@ -34,7 +34,7 @@ When rendering a popover, you can think of it made of three layers: 2. the layout 3. the content -You handle the two former elements, and the first one is fully handled by the library. +The first one is fully handled by the library, but you have control over the last two elements. The positioned element has the `.coupdoeil--popover` CSS class, a `data-placement` attribute that is set to the current popover position relative to its target, -- GitLab