decidim-dev 0.31.5 → 0.32.0.rc2

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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -12
  3. data/app/models/decidim/dev/dummy_resource.rb +4 -1
  4. data/app/models/decidim/dev/nested_dummy_resource.rb +1 -0
  5. data/config/rubocop/decidim-linters/configuration.yml +8 -0
  6. data/config/rubocop/rails/disabled.yml +6 -0
  7. data/config/rubocop/rspec/configuration.yml +5 -0
  8. data/config/rubocop/ruby/configuration.yml +2 -3
  9. data/config/rubocop/ruby/disabled.yml +9 -0
  10. data/config/rubocop/yard/configuration.yml +1 -0
  11. data/decidim-dev.gemspec +21 -23
  12. data/lib/decidim/dev/assets/assemblies.json +1 -21
  13. data/lib/decidim/dev/assets/assemblies_with_null.json +1 -2
  14. data/lib/decidim/dev/assets/participatory_processes.json +1 -4
  15. data/lib/decidim/dev/assets/participatory_processes_with_null.json +1 -1
  16. data/lib/decidim/dev/dummy_translator.rb +1 -1
  17. data/lib/decidim/dev/rubocop/cop/decidim/message_antipattern.rb +76 -0
  18. data/lib/decidim/dev/test/rspec_support/active_job.rb +5 -0
  19. data/lib/decidim/dev/test/rspec_support/component_context.rb +1 -1
  20. data/lib/decidim/dev/test/rspec_support/helpers.rb +9 -3
  21. data/lib/decidim/dev/test/rspec_support/tom_select.rb +7 -4
  22. data/lib/decidim/dev/version.rb +1 -1
  23. data/lib/decidim/dev.rb +0 -1
  24. data/lib/erb_lint/linters/admin_page_title_linter.rb +46 -0
  25. data/lib/erb_lint/linters/partial_path_linter.rb +101 -0
  26. data/rubocop-decidim.yml +2 -0
  27. metadata +134 -64
  28. /data/lib/decidim/dev/assets/{import_participatory_space_private_users.csv → import_members.csv} +0 -0
  29. /data/lib/decidim/dev/assets/{import_participatory_space_private_users_invalid_col_sep.csv → import_members_invalid_col_sep.csv} +0 -0
  30. /data/lib/decidim/dev/assets/{import_participatory_space_private_users_iso8859-1.csv → import_members_iso8859-1.csv} +0 -0
  31. /data/lib/decidim/dev/assets/{import_participatory_space_private_users_nok.csv → import_members_nok.csv} +0 -0
  32. /data/lib/decidim/dev/assets/{import_participatory_space_private_users_with_bom.csv → import_members_with_bom.csv} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9e8127bdb518b7b911245d133b6ed6eb76910da76626430cd9fb0f50b22fbdbf
4
- data.tar.gz: 231f280eab82edf3bdb0548640468bde75e513735b024fe6c7c850625cdf5302
3
+ metadata.gz: 9ff7a228d9198029691d6566d4c166dde22f3aefb13f5978f9582370ef70bbaf
4
+ data.tar.gz: 2cb1bcea9d5edce2b33540a0c62b107e7bb5505db5ae5557d281dfd7b1b8737d
5
5
  SHA512:
6
- metadata.gz: c51ec280d4140e452119338702f0618f98f1bcecc2a47d7a370e9b9fd19af8adc9b43204d6bd7b653d63bc7bc5ad5b74b729206390e51ea6190608499789ae8e
7
- data.tar.gz: 597209ad93a487b7013107169cb92b3ba5677d1fd32e8671ba7a4b33acc303673a299076d77b12dd69cc1d916f6e9f285e34c25e4b9a0fe1ea4391f1a50694f7
6
+ metadata.gz: ef70a3897e995857c6b145a086982c3691e9654d1f5d529d08209562cdd3ea1f54aa10a5431d7af4960261889a3ce7f2ad3caaff965e3826a83e9d0c135d4fa2
7
+ data.tar.gz: b8cd78e7a7bd46a1bc328194c8aa914a03dde4179c7283a71e45f64440ca48bba2e458055b3dbba4cf9b3065e8fbad876d97b884995dc705382605f76287dd36
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Decidim::Dev
1
+ # decidim-dev
2
2
 
3
3
  This module allows local development of decidim's components and other features.
4
4
 
@@ -9,19 +9,11 @@ dummy assets or resources that you can use to test your own modules. It also
9
9
  provides a `decidim:generate_external_test_app` rails task that you can use to
10
10
  generate a dummy application to test your module.
11
11
 
12
- ## Installation
13
-
14
- Add this line to your application's Gemfile:
12
+ This is a module oriented for developers, as a building block to be used by other modules.
15
13
 
16
- ```ruby
17
- gem 'decidim-dev'
18
- ```
19
-
20
- And then execute:
14
+ ## Installation
21
15
 
22
- ```bash
23
- bundle
24
- ```
16
+ This is on the default Decidim installation so you should not change anything to use this module.
25
17
 
26
18
  ## Contributing
27
19
 
@@ -78,7 +78,10 @@ module Decidim
78
78
 
79
79
  # Public: Whether the object can have new comment votes or not.
80
80
  def user_allowed_to_vote_comment?(user)
81
- component.can_participate_in_space?(user)
81
+ return false unless accepts_new_comments?
82
+ return unless component.can_participate_in_space?(user)
83
+
84
+ ActionAuthorizer.new(user, "vote_comment", component, self).authorize.ok?
82
85
  end
83
86
 
84
87
  def self.user_collection(user)
@@ -4,6 +4,7 @@ module Decidim
4
4
  module Dev
5
5
  class NestedDummyResource < ApplicationRecord
6
6
  include Decidim::Resourceable
7
+
7
8
  belongs_to :dummy_resource
8
9
  end
9
10
  end
@@ -0,0 +1,8 @@
1
+ require:
2
+ - decidim/dev/rubocop/cop/decidim/message_antipattern
3
+
4
+ Decidim/MessageAntipattern:
5
+ Enabled: true
6
+ Include:
7
+ - "**/spec/**/*"
8
+ - "**/test/**/*"
@@ -44,3 +44,9 @@ Rails/EnumHash:
44
44
 
45
45
  Rails/I18nLazyLookup:
46
46
  Enabled: false
47
+
48
+ Rails/FindByOrAssignmentMemoization:
49
+ Enabled: false
50
+
51
+ Rails/Exit:
52
+ Enabled: false
@@ -50,3 +50,8 @@ RSpec/RepeatedExampleGroupDescription:
50
50
 
51
51
  RSpec/RepeatedExampleGroupBody:
52
52
  Enabled: true
53
+
54
+ RSpec/ExpectActual:
55
+ Enabled: true
56
+ Exclude:
57
+ - decidim-core/spec/system/homepage_spec.rb
@@ -1,5 +1,3 @@
1
- require: rubocop
2
-
3
1
  # Common configuration.
4
2
  AllCops:
5
3
  Include:
@@ -68,7 +66,7 @@ AllCops:
68
66
  # If a value is specified for TargetRubyVersion then it is used.
69
67
  # Else if .ruby-version exists and it contains an MRI version it is used.
70
68
  # Otherwise we fallback to the oldest officially supported Ruby version (2.0).
71
- TargetRubyVersion: 3.3
69
+ TargetRubyVersion: 3.4
72
70
 
73
71
  # Indent private/protected/public as deep as method definitions
74
72
  Layout/AccessModifierIndentation:
@@ -521,6 +519,7 @@ Style/GuardClause:
521
519
  MinBodyLength: 6
522
520
 
523
521
  Style/HashSyntax:
522
+ EnforcedShorthandSyntax: always
524
523
  EnforcedStyle: no_mixed_keys
525
524
  SupportedStyles:
526
525
  # checks for 1.9 syntax (e.g. {a: 1}) for all symbol keys
@@ -104,3 +104,12 @@ Style/EmptyStringInsideInterpolation:
104
104
 
105
105
  Style/ComparableBetween:
106
106
  Enabled: false
107
+
108
+ Style/FileOpen:
109
+ Enabled: false
110
+
111
+ Style/ModuleMemberExistenceCheck:
112
+ Enabled: false
113
+
114
+ Style/OneClassPerFile:
115
+ Enabled: false
@@ -0,0 +1 @@
1
+ plugins: rubocop-yard
data/decidim-dev.gemspec CHANGED
@@ -2,12 +2,9 @@
2
2
 
3
3
  $LOAD_PATH.push File.expand_path("lib", __dir__)
4
4
 
5
- # Maintain your gem's version:
6
- require "decidim/dev/version"
7
-
8
- # Describe your gem and declare its dependencies:
9
5
  Gem::Specification.new do |s|
10
- s.version = Decidim::Dev.version
6
+ version = "0.32.0.rc2"
7
+ s.version = version
11
8
  s.authors = ["Josep Jaume Rey Peroy", "Marc Riera Casals", "Oriol Gual Oliva"]
12
9
  s.email = ["josepjaume@gmail.com", "mrc2407@gmail.com", "oriolgual@gmail.com"]
13
10
  s.license = "AGPL-3.0-or-later"
@@ -19,7 +16,7 @@ Gem::Specification.new do |s|
19
16
  "homepage_uri" => "https://decidim.org",
20
17
  "source_code_uri" => "https://github.com/decidim/decidim"
21
18
  }
22
- s.required_ruby_version = "~> 3.3.0"
19
+ s.required_ruby_version = "~> 3.4.0"
23
20
 
24
21
  s.name = "decidim-dev"
25
22
  s.summary = "Decidim dev tools"
@@ -33,42 +30,43 @@ Gem::Specification.new do |s|
33
30
  end
34
31
 
35
32
  s.add_dependency "capybara", "~> 3.39"
36
- s.add_dependency "decidim-admin", Decidim::Dev.version
37
- s.add_dependency "decidim-api", Decidim::Dev.version
38
- s.add_dependency "decidim-comments", Decidim::Dev.version
39
- s.add_dependency "decidim-core", Decidim::Dev.version
40
- s.add_dependency "decidim-generators", Decidim::Dev.version
41
- s.add_dependency "decidim-verifications", Decidim::Dev.version
33
+ s.add_dependency "decidim-admin", version
34
+ s.add_dependency "decidim-api", version
35
+ s.add_dependency "decidim-comments", version
36
+ s.add_dependency "decidim-core", version
37
+ s.add_dependency "decidim-generators", version
38
+ s.add_dependency "decidim-verifications", version
42
39
  s.add_dependency "factory_bot_rails", "~> 6.2"
43
40
  s.add_dependency "faker", "~> 3.2"
44
41
 
45
- s.add_dependency "bullet", "~> 8.0.0"
46
- s.add_dependency "byebug", "~> 11.0"
47
- s.add_dependency "erb_lint", "~> 0.8.0"
42
+ s.add_dependency "bullet", "~> 8.1.0"
43
+ s.add_dependency "byebug", ">= 11", "< 14"
44
+ s.add_dependency "erb_lint", ">= 0.8", "< 0.10"
48
45
  s.add_dependency "i18n-tasks", "~> 1.0"
49
46
  s.add_dependency "nokogiri", "~> 1.16", ">= 1.16.2"
50
- s.add_dependency "parallel_tests", "~> 4.2"
51
- s.add_dependency "puma", "~> 6.5"
47
+ s.add_dependency "parallel_tests", ">= 4.2", "< 6.0"
48
+ s.add_dependency "puma", ">= 6.5", "< 8.0"
52
49
  s.add_dependency "rails-controller-testing", "~> 1.0"
53
50
  s.add_dependency "rspec", "~> 3.12"
54
51
  s.add_dependency "rspec-cells", "~> 0.3.7"
55
52
  s.add_dependency "rspec-html-matchers", "~> 0.10"
56
53
  s.add_dependency "rspec_junit_formatter", "~> 0.6.0"
57
- s.add_dependency "rspec-rails", "~> 6.0"
54
+ s.add_dependency "rspec-rails", ">= 6", "< 9"
58
55
  s.add_dependency "rspec-retry", "~> 0.6.2"
59
- s.add_dependency "rubocop", "~> 1.78.0"
56
+ s.add_dependency "rubocop", ">= 1.78", "< 1.87"
60
57
  s.add_dependency "rubocop-capybara", "~> 2.22.0", ">= 2.22.1"
61
- s.add_dependency "rubocop-factory_bot", "~> 2.27.0"
58
+ s.add_dependency "rubocop-factory_bot", ">= 2.27", "< 2.29"
62
59
  s.add_dependency "rubocop-faker", "~> 1.3", ">= 1.3.0"
63
60
  s.add_dependency "rubocop-graphql", "~> 1.5", ">= 1.5.6"
64
61
  s.add_dependency "rubocop-performance", "~> 1.25", ">= 1.25.0"
65
- s.add_dependency "rubocop-rails", "~> 2.32.0", ">= 2.32.0"
62
+ s.add_dependency "rubocop-rails", ">= 2.32", "< 2.35"
66
63
  s.add_dependency "rubocop-rspec", "~> 3.0", ">= 3.6.0"
67
- s.add_dependency "rubocop-rspec_rails", "~> 2.31.0"
64
+ s.add_dependency "rubocop-rspec_rails", ">= 2.31", "< 2.33"
68
65
  s.add_dependency "rubocop-rubycw", "~> 0.2.0"
66
+ s.add_dependency "rubocop-yard", ">= 1.0", "< 1.2"
69
67
  s.add_dependency "selenium-webdriver", "~> 4.9"
70
68
  s.add_dependency "simplecov", "~> 0.22.0"
71
- s.add_dependency "simplecov-cobertura", "~> 2.1.0"
69
+ s.add_dependency "simplecov-cobertura", "~> 3.1.0"
72
70
  s.add_dependency "spring", "~> 4.0"
73
71
  s.add_dependency "spring-watcher-listen", "~> 2.0"
74
72
  s.add_dependency "w3c_rspec_validators", "~> 0.3.0"
@@ -47,6 +47,7 @@
47
47
  "es": "Incidunt provident doloremque."
48
48
  },
49
49
  "decidim_scope_id": null,
50
+ "access_mode": "open",
50
51
  "paticipatory_scope": {
51
52
  "ca": "Assumenda.",
52
53
  "en": "Quidem.",
@@ -58,7 +59,6 @@
58
59
  "es": "Et molestiae."
59
60
  },
60
61
  "scopes_enabled": true,
61
- "private_space": false,
62
62
  "reference": "PhD-ASSE-2020-01-1",
63
63
  "purpose_of_action": {
64
64
  "ca": "<p>Velit a qui. Debitis rem occaecati. Id minus est.</p>",
@@ -90,7 +90,6 @@
90
90
  "en": "<p>Optio ducimus velit. Facilis omnis asperiores. Corporis ipsa non.</p>",
91
91
  "es": "<p>Est dolorum et. Quo sed ut. Corporis quos sit.</p>"
92
92
  },
93
- "is_transparent": true,
94
93
  "special_features": {
95
94
  "ca": "<p>Dolor et reprehenderit. Unde soluta eum. Omnis occaecati omnis.</p>",
96
95
  "en": "<p>Et atque perspiciatis. Quas impedit corporis. Nesciunt blanditiis ipsa.</p>",
@@ -106,7 +105,6 @@
106
105
  "en": "ut",
107
106
  "es": "rerum"
108
107
  },
109
- "decidim_assemblies_type_id": 1,
110
108
  "area": {
111
109
  "id": null,
112
110
  "name": {
@@ -514,7 +512,6 @@
514
512
  "geocoding_enabled": false,
515
513
  "attachments_allowed": false,
516
514
  "resources_permissions_enabled": true,
517
- "collaborative_drafts_enabled": true,
518
515
  "participatory_texts_enabled": false,
519
516
  "amendments_enabled": false,
520
517
  "amendments_wizard_help_text": {
@@ -578,23 +575,6 @@
578
575
  "permissions": null,
579
576
  "published_at": "2020-01-22 07:55:05 UTC"
580
577
  },
581
- {
582
- "manifest_name": "sortitions",
583
- "id": 26,
584
- "name": {
585
- "ca": "Sortejos",
586
- "en": "Sortitions",
587
- "es": "Sorteos"
588
- },
589
- "participatory_space_id": 1,
590
- "participatory_space_type": "Decidim::Assembly",
591
- "settings": {
592
- "comments_enabled": true
593
- },
594
- "weight": 0,
595
- "permissions": null,
596
- "published_at": "2020-01-22 07:55:19 UTC"
597
- },
598
578
  {
599
579
  "manifest_name": "surveys",
600
580
  "id": 23,
@@ -58,7 +58,7 @@
58
58
  "es": "Et molestiae."
59
59
  },
60
60
  "scopes_enabled": true,
61
- "private_space": false,
61
+ "access_mode": "open",
62
62
  "reference": "PhD-ASSE-2020-01-1",
63
63
  "purpose_of_action": {
64
64
  "ca": "<p>Velit a qui. Debitis rem occaecati. Id minus est.</p>",
@@ -106,7 +106,6 @@
106
106
  "en": "ut",
107
107
  "es": "rerum"
108
108
  },
109
- "decidim_assemblies_type_id": 1,
110
109
  "area": {
111
110
  "id": null,
112
111
  "name": {
@@ -87,7 +87,7 @@
87
87
  "es": "South Carolina"
88
88
  }
89
89
  },
90
- "private_space": false,
90
+ "access_mode": "open",
91
91
  "promoted": true,
92
92
  "scopes_enabled": true,
93
93
  "participatory_process_steps": [
@@ -105,9 +105,6 @@
105
105
  },
106
106
  "start_date": "2019-09-02",
107
107
  "end_date": "2019-12-02",
108
- "cta_path": null,
109
- "cta_text": {
110
- },
111
108
  "active": true,
112
109
  "position": 0
113
110
  }
@@ -87,7 +87,7 @@
87
87
  "es": "South Carolina"
88
88
  }
89
89
  },
90
- "private_space": false,
90
+ "access_mode": "open",
91
91
  "promoted": true,
92
92
  "scopes_enabled": true,
93
93
  "participatory_process_steps": null,
@@ -20,7 +20,7 @@ module Decidim
20
20
  def translate
21
21
  translated_text = "#{target_locale} - #{text}"
22
22
 
23
- MachineTranslationSaveJob.perform_later(
23
+ Decidim::MachineTranslationSaveJob.perform_later(
24
24
  resource,
25
25
  field_name,
26
26
  target_locale,
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubocop"
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module Decidim
8
+ class MessageAntipattern < RuboCop::Cop::Base
9
+ SINGLE_WORD_ANTI_PATTERNS = %w(
10
+ successfully
11
+ problem
12
+ error
13
+ warning
14
+ done
15
+ complete
16
+ finished
17
+ ok
18
+ okay
19
+ saved
20
+ updated
21
+ created
22
+ deleted
23
+ removed
24
+ published
25
+ unpublished
26
+ ).freeze
27
+
28
+ MSG = "Anti-pattern detected: avoid generic single-word text in have_callout/have_admin_callout/have_content. " \
29
+ "Use the full admin flash message, e.g. 'Meeting successfully published'. " \
30
+ "Exception: when used inside `within` blocks (e.g., for checking `.label` elements)."
31
+
32
+ def on_send(node)
33
+ return unless [:have_callout, :have_admin_callout, :have_content].include?(node.method_name)
34
+ return if within_block?(node)
35
+
36
+ first_argument = node.first_argument
37
+ return unless first_argument
38
+
39
+ if first_argument.nil_type?
40
+ add_offense(first_argument, message: MSG)
41
+ return
42
+ end
43
+ return unless first_argument.str_type?
44
+
45
+ text = first_argument.value
46
+ return unless antipattern_text?(text)
47
+
48
+ add_offense(first_argument, message: MSG)
49
+ end
50
+
51
+ private
52
+
53
+ def within_block?(node)
54
+ node.each_ancestor(:block).any? do |ancestor|
55
+ ancestor.send_node&.method_name == :within
56
+ end
57
+ end
58
+
59
+ def antipattern_text?(text)
60
+ return true if text.nil?
61
+ return true if text.empty?
62
+
63
+ stripped_text = text.gsub(/[[:punct:]\s]/, "")
64
+ return true if stripped_text.empty?
65
+
66
+ single_word = text.strip !~ /\s/
67
+ return false unless single_word
68
+
69
+ return true if SINGLE_WORD_ANTI_PATTERNS.include?(stripped_text.downcase)
70
+
71
+ false
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -14,6 +14,11 @@ end
14
14
  RSpec.configure do |config|
15
15
  config.include ActiveJob::TestHelper
16
16
  config.include DecidimActiveJobExtensions
17
+
18
+ config.before do
19
+ clear_enqueued_jobs
20
+ clear_performed_jobs
21
+ end
17
22
  end
18
23
 
19
24
  ActiveJob::Base.queue_adapter = :test
@@ -58,7 +58,7 @@ shared_context "with a component" do
58
58
  end
59
59
 
60
60
  def visit_component
61
- page.visit main_component_path(component)
61
+ page.visit main_component_path(component, locale: I18n.locale)
62
62
  end
63
63
  end
64
64
 
@@ -33,8 +33,8 @@ module Decidim
33
33
  end
34
34
 
35
35
  def within_language_menu(options = {})
36
- within(options[:admin] ? ".language-choose" : "footer") do
37
- find(options[:admin] ? "#admin-menu-trigger" : "#trigger-dropdown-language-chooser").click
36
+ within(options[:admin] ? ".language-choose" : "header") do
37
+ find(options[:admin] ? "#admin-menu-trigger" : "#trigger-dropdown-menu-language-chooser-desktop").click
38
38
  yield
39
39
  end
40
40
  end
@@ -53,12 +53,18 @@ module Decidim
53
53
  expect(page).to have_css(".main-bar #trigger-dropdown-account")
54
54
  end
55
55
 
56
- def have_admin_callout(text)
56
+ def have_callout(text)
57
57
  within_flash_messages do
58
58
  have_content text
59
59
  end
60
60
  end
61
61
 
62
+ # Fallback for legacy usage of this helper
63
+ # It actually works the same, as the markup is the same too
64
+ def have_admin_callout(text)
65
+ have_callout(text)
66
+ end
67
+
62
68
  def stub_get_request_with_format(rq_url, rs_format)
63
69
  stub_request(:get, rq_url)
64
70
  .with(
@@ -9,11 +9,14 @@ module Capybara
9
9
  # a better way, we could try actually interacting with the on-screen tom-select-provided UI,
10
10
  # but we are taking the easy way out for now.
11
11
  #
12
- # @param option_id can be the `id` value of an option in the select, OR for select multiple inputs,
13
- # can be an array of such IDs.
12
+ # @param select_selector [String] The CSS selector of the select element.
13
+ # @param option_id [String, Array<String>] The `id` value of an option in the select.
14
+ # For selects with the `multiple` attribute, this can be an array of such IDs.
14
15
  #
15
- # @example tom_select("#select_id", option_id: "2")
16
- # @example tom_select("#select_id", option_id: ["2", "10"]) # `multiple` input.
16
+ # @example
17
+ # tom_select("#select_id", option_id: "2")
18
+ # @example
19
+ # tom_select("#select_id", option_id: ["2", "10"]) # For a `multiple` select input.
17
20
  def tom_select(select_selector, option_id:)
18
21
  js_str = %(document.querySelector("#{select_selector}").tomselect.setValue(#{option_id.inspect}))
19
22
  execute_script(js_str)
@@ -4,7 +4,7 @@ module Decidim
4
4
  # This holds the decidim-dev version.
5
5
  module Dev
6
6
  def self.version
7
- "0.31.5"
7
+ "0.32.0.rc2"
8
8
  end
9
9
  end
10
10
  end
data/lib/decidim/dev.rb CHANGED
@@ -22,7 +22,6 @@ module Decidim
22
22
  # create external libraries that create test apps and test themselves against
23
23
  # them.
24
24
  module Dev
25
- include ActiveSupport::Configurable
26
25
  autoload :DummyTranslator, "decidim/dev/dummy_translator"
27
26
 
28
27
  # Public: Finds an asset.
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb_lint"
4
+ require "erb_lint/linter"
5
+ require "erb_lint/linter_config"
6
+ require "erb_lint/linter_registry"
7
+ require "erb_lint/offense"
8
+
9
+ module ERBLint
10
+ module Linters
11
+ class AdminPageTitleLinter < Linter
12
+ include LinterRegistry
13
+
14
+ TITLE_SNIPPET = '<% add_decidim_page_title(t(".title")) %>'
15
+ TITLE_SNIPPET_REGEX = /\A<%\s*add_decidim_page_title\(t\(".title".*?\)\)\s*%>/
16
+
17
+ def run(processed_source)
18
+ return unless admin_view?(processed_source.filename)
19
+
20
+ first_line = processed_source.file_content.to_s.lines.first
21
+ return if first_line&.match?(TITLE_SNIPPET_REGEX)
22
+
23
+ add_offense(
24
+ processed_source.to_source_range(0...0),
25
+ "Admin views must start with: #{TITLE_SNIPPET}"
26
+ )
27
+ end
28
+
29
+ private
30
+
31
+ def admin_view?(filename)
32
+ return false unless filename.include?("/app/views/")
33
+ return false unless filename.include?("/admin/")
34
+ return false if filename.include?("/layouts/")
35
+ return false if mailer_view?(filename)
36
+ return false unless filename.end_with?(".html.erb")
37
+
38
+ File.basename(filename).start_with?("_") == false
39
+ end
40
+
41
+ def mailer_view?(filename)
42
+ filename.include?("/mailer/") || filename =~ %r{/\w+_mailer/}
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb_lint"
4
+ require "erb_lint/linter"
5
+ require "erb_lint/linter_config"
6
+ require "erb_lint/linter_registry"
7
+ require "erb_lint/offense"
8
+
9
+ module ERBLint
10
+ module Linters
11
+ # Lint ERB partial paths.
12
+ #
13
+ # This linter ensures that partials are rendered with their full path
14
+ # relative to the views directory. For example, in app/views/posts/index.html.erb,
15
+ # it will flag `render "post"` and suggest `render "posts/post"`.
16
+ #
17
+ # It allows configuring prefixes that are exempt from this rule via
18
+ # the `allowed_prefixes` configuration option.
19
+ class PartialPath < Linter
20
+ include LinterRegistry
21
+
22
+ # Configuration schema for the PartialPath linter.
23
+ #
24
+ # @!attribute allowed_prefixes
25
+ # @return [Array<String>] List of prefixes that are allowed to be used without full path
26
+ class ConfigSchema < LinterConfig
27
+ property :allowed_prefixes, accepts: Array
28
+ end
29
+ self.config_schema = ConfigSchema
30
+
31
+ # Runs the linter on the given processed source.
32
+ #
33
+ # This method scans the source code for ERB render calls and checks if they
34
+ # are using the full path for partials. If not, it adds an offense.
35
+ #
36
+ # @param processed_source [ERBLint::ProcessedSource] The processed source to lint
37
+ # @return [void]
38
+ def run(processed_source)
39
+ file_path = processed_source.filename
40
+
41
+ return unless file_path =~ %r{app/views/(.+?)/_?([^/]+)\.html\.erb$}
42
+ return if file_path.include?("/cells/")
43
+
44
+ current_directory = Regexp.last_match(1)
45
+ source = processed_source.file_content
46
+
47
+ source.scan(/<%=\s*render\s+"([^"]+)"[^%]*%>/) do
48
+ partial_path = Regexp.last_match(1)
49
+ start_pos = Regexp.last_match.begin(1)
50
+
51
+ next if partial_path.start_with?("layouts/")
52
+ next if partial_path.start_with?("/")
53
+ next if partial_path.include?("/")
54
+
55
+ next if allowed_prefix?(partial_path)
56
+
57
+ full_path = "#{current_directory}/#{partial_path}"
58
+ range = processed_source.to_source_range(
59
+ start_pos...(start_pos + partial_path.length)
60
+ )
61
+
62
+ add_offense(
63
+ range,
64
+ "Use the full path for partials. Replace `render \"#{partial_path}\"` with `render \"#{full_path}\"`"
65
+ )
66
+ end
67
+ end
68
+
69
+ # Checks if the given path starts with any of the allowed prefixes.
70
+ #
71
+ # @param path [String] The partial path to check
72
+ # @return [Boolean] true if the path starts with an allowed prefix, false otherwise
73
+ def allowed_prefix?(path)
74
+ return false unless @config.allowed_prefixes
75
+
76
+ @config.allowed_prefixes.any? { |prefix| path.start_with?(prefix) }
77
+ end
78
+
79
+ # Generates the autocorrection lambda for the given offense.
80
+ #
81
+ # This method creates a lambda that will replace the partial path with its
82
+ # full path when the linter's autocorrect feature is used.
83
+ #
84
+ # @param processed_source [ERBLint::ProcessedSource] The processed source containing the offense
85
+ # @param offense [ERBLint::Offense] The offense to generate a correction for
86
+ # @return [Proc, nil] A lambda that performs the correction, or nil if correction is not possible
87
+ def autocorrect(processed_source, offense)
88
+ return unless processed_source.filename =~ %r{app/views/(.+?)/_?([^/]+)\.html\.erb$}
89
+ return if processed_source.filename.include?("/cells/")
90
+
91
+ current_directory = Regexp.last_match(1)
92
+
93
+ lambda do |corrector|
94
+ partial_path = offense.source_range.source
95
+ full_path = "#{current_directory}/#{partial_path}"
96
+ corrector.replace(offense.source_range, full_path)
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
data/rubocop-decidim.yml CHANGED
@@ -25,4 +25,6 @@ inherit_from:
25
25
  - config/rubocop/graphql/disabled.yml
26
26
  - config/rubocop/factory_bot/configuration.yml
27
27
  - config/rubocop/factory_bot/disabled.yml
28
+ - config/rubocop/yard/configuration.yml
28
29
  - config/rubocop/disabled.yml
30
+ - config/rubocop/decidim-linters/configuration.yml
metadata CHANGED
@@ -1,16 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: decidim-dev
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.31.5
4
+ version: 0.32.0.rc2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josep Jaume Rey Peroy
8
8
  - Marc Riera Casals
9
9
  - Oriol Gual Oliva
10
- autorequire:
11
10
  bindir: bin
12
11
  cert_chain: []
13
- date: 2026-05-12 00:00:00.000000000 Z
12
+ date: 1980-01-02 00:00:00.000000000 Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: capybara
@@ -32,84 +31,84 @@ dependencies:
32
31
  requirements:
33
32
  - - '='
34
33
  - !ruby/object:Gem::Version
35
- version: 0.31.5
34
+ version: 0.32.0.rc2
36
35
  type: :runtime
37
36
  prerelease: false
38
37
  version_requirements: !ruby/object:Gem::Requirement
39
38
  requirements:
40
39
  - - '='
41
40
  - !ruby/object:Gem::Version
42
- version: 0.31.5
41
+ version: 0.32.0.rc2
43
42
  - !ruby/object:Gem::Dependency
44
43
  name: decidim-api
45
44
  requirement: !ruby/object:Gem::Requirement
46
45
  requirements:
47
46
  - - '='
48
47
  - !ruby/object:Gem::Version
49
- version: 0.31.5
48
+ version: 0.32.0.rc2
50
49
  type: :runtime
51
50
  prerelease: false
52
51
  version_requirements: !ruby/object:Gem::Requirement
53
52
  requirements:
54
53
  - - '='
55
54
  - !ruby/object:Gem::Version
56
- version: 0.31.5
55
+ version: 0.32.0.rc2
57
56
  - !ruby/object:Gem::Dependency
58
57
  name: decidim-comments
59
58
  requirement: !ruby/object:Gem::Requirement
60
59
  requirements:
61
60
  - - '='
62
61
  - !ruby/object:Gem::Version
63
- version: 0.31.5
62
+ version: 0.32.0.rc2
64
63
  type: :runtime
65
64
  prerelease: false
66
65
  version_requirements: !ruby/object:Gem::Requirement
67
66
  requirements:
68
67
  - - '='
69
68
  - !ruby/object:Gem::Version
70
- version: 0.31.5
69
+ version: 0.32.0.rc2
71
70
  - !ruby/object:Gem::Dependency
72
71
  name: decidim-core
73
72
  requirement: !ruby/object:Gem::Requirement
74
73
  requirements:
75
74
  - - '='
76
75
  - !ruby/object:Gem::Version
77
- version: 0.31.5
76
+ version: 0.32.0.rc2
78
77
  type: :runtime
79
78
  prerelease: false
80
79
  version_requirements: !ruby/object:Gem::Requirement
81
80
  requirements:
82
81
  - - '='
83
82
  - !ruby/object:Gem::Version
84
- version: 0.31.5
83
+ version: 0.32.0.rc2
85
84
  - !ruby/object:Gem::Dependency
86
85
  name: decidim-generators
87
86
  requirement: !ruby/object:Gem::Requirement
88
87
  requirements:
89
88
  - - '='
90
89
  - !ruby/object:Gem::Version
91
- version: 0.31.5
90
+ version: 0.32.0.rc2
92
91
  type: :runtime
93
92
  prerelease: false
94
93
  version_requirements: !ruby/object:Gem::Requirement
95
94
  requirements:
96
95
  - - '='
97
96
  - !ruby/object:Gem::Version
98
- version: 0.31.5
97
+ version: 0.32.0.rc2
99
98
  - !ruby/object:Gem::Dependency
100
99
  name: decidim-verifications
101
100
  requirement: !ruby/object:Gem::Requirement
102
101
  requirements:
103
102
  - - '='
104
103
  - !ruby/object:Gem::Version
105
- version: 0.31.5
104
+ version: 0.32.0.rc2
106
105
  type: :runtime
107
106
  prerelease: false
108
107
  version_requirements: !ruby/object:Gem::Requirement
109
108
  requirements:
110
109
  - - '='
111
110
  - !ruby/object:Gem::Version
112
- version: 0.31.5
111
+ version: 0.32.0.rc2
113
112
  - !ruby/object:Gem::Dependency
114
113
  name: factory_bot_rails
115
114
  requirement: !ruby/object:Gem::Requirement
@@ -144,42 +143,54 @@ dependencies:
144
143
  requirements:
145
144
  - - "~>"
146
145
  - !ruby/object:Gem::Version
147
- version: 8.0.0
146
+ version: 8.1.0
148
147
  type: :runtime
149
148
  prerelease: false
150
149
  version_requirements: !ruby/object:Gem::Requirement
151
150
  requirements:
152
151
  - - "~>"
153
152
  - !ruby/object:Gem::Version
154
- version: 8.0.0
153
+ version: 8.1.0
155
154
  - !ruby/object:Gem::Dependency
156
155
  name: byebug
157
156
  requirement: !ruby/object:Gem::Requirement
158
157
  requirements:
159
- - - "~>"
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: '11'
161
+ - - "<"
160
162
  - !ruby/object:Gem::Version
161
- version: '11.0'
163
+ version: '14'
162
164
  type: :runtime
163
165
  prerelease: false
164
166
  version_requirements: !ruby/object:Gem::Requirement
165
167
  requirements:
166
- - - "~>"
168
+ - - ">="
167
169
  - !ruby/object:Gem::Version
168
- version: '11.0'
170
+ version: '11'
171
+ - - "<"
172
+ - !ruby/object:Gem::Version
173
+ version: '14'
169
174
  - !ruby/object:Gem::Dependency
170
175
  name: erb_lint
171
176
  requirement: !ruby/object:Gem::Requirement
172
177
  requirements:
173
- - - "~>"
178
+ - - ">="
174
179
  - !ruby/object:Gem::Version
175
- version: 0.8.0
180
+ version: '0.8'
181
+ - - "<"
182
+ - !ruby/object:Gem::Version
183
+ version: '0.10'
176
184
  type: :runtime
177
185
  prerelease: false
178
186
  version_requirements: !ruby/object:Gem::Requirement
179
187
  requirements:
180
- - - "~>"
188
+ - - ">="
189
+ - !ruby/object:Gem::Version
190
+ version: '0.8'
191
+ - - "<"
181
192
  - !ruby/object:Gem::Version
182
- version: 0.8.0
193
+ version: '0.10'
183
194
  - !ruby/object:Gem::Dependency
184
195
  name: i18n-tasks
185
196
  requirement: !ruby/object:Gem::Requirement
@@ -218,30 +229,42 @@ dependencies:
218
229
  name: parallel_tests
219
230
  requirement: !ruby/object:Gem::Requirement
220
231
  requirements:
221
- - - "~>"
232
+ - - ">="
222
233
  - !ruby/object:Gem::Version
223
234
  version: '4.2'
235
+ - - "<"
236
+ - !ruby/object:Gem::Version
237
+ version: '6.0'
224
238
  type: :runtime
225
239
  prerelease: false
226
240
  version_requirements: !ruby/object:Gem::Requirement
227
241
  requirements:
228
- - - "~>"
242
+ - - ">="
229
243
  - !ruby/object:Gem::Version
230
244
  version: '4.2'
245
+ - - "<"
246
+ - !ruby/object:Gem::Version
247
+ version: '6.0'
231
248
  - !ruby/object:Gem::Dependency
232
249
  name: puma
233
250
  requirement: !ruby/object:Gem::Requirement
234
251
  requirements:
235
- - - "~>"
252
+ - - ">="
236
253
  - !ruby/object:Gem::Version
237
254
  version: '6.5'
255
+ - - "<"
256
+ - !ruby/object:Gem::Version
257
+ version: '8.0'
238
258
  type: :runtime
239
259
  prerelease: false
240
260
  version_requirements: !ruby/object:Gem::Requirement
241
261
  requirements:
242
- - - "~>"
262
+ - - ">="
243
263
  - !ruby/object:Gem::Version
244
264
  version: '6.5'
265
+ - - "<"
266
+ - !ruby/object:Gem::Version
267
+ version: '8.0'
245
268
  - !ruby/object:Gem::Dependency
246
269
  name: rails-controller-testing
247
270
  requirement: !ruby/object:Gem::Requirement
@@ -316,16 +339,22 @@ dependencies:
316
339
  name: rspec-rails
317
340
  requirement: !ruby/object:Gem::Requirement
318
341
  requirements:
319
- - - "~>"
342
+ - - ">="
320
343
  - !ruby/object:Gem::Version
321
- version: '6.0'
344
+ version: '6'
345
+ - - "<"
346
+ - !ruby/object:Gem::Version
347
+ version: '9'
322
348
  type: :runtime
323
349
  prerelease: false
324
350
  version_requirements: !ruby/object:Gem::Requirement
325
351
  requirements:
326
- - - "~>"
352
+ - - ">="
327
353
  - !ruby/object:Gem::Version
328
- version: '6.0'
354
+ version: '6'
355
+ - - "<"
356
+ - !ruby/object:Gem::Version
357
+ version: '9'
329
358
  - !ruby/object:Gem::Dependency
330
359
  name: rspec-retry
331
360
  requirement: !ruby/object:Gem::Requirement
@@ -344,16 +373,22 @@ dependencies:
344
373
  name: rubocop
345
374
  requirement: !ruby/object:Gem::Requirement
346
375
  requirements:
347
- - - "~>"
376
+ - - ">="
348
377
  - !ruby/object:Gem::Version
349
- version: 1.78.0
378
+ version: '1.78'
379
+ - - "<"
380
+ - !ruby/object:Gem::Version
381
+ version: '1.87'
350
382
  type: :runtime
351
383
  prerelease: false
352
384
  version_requirements: !ruby/object:Gem::Requirement
353
385
  requirements:
354
- - - "~>"
386
+ - - ">="
355
387
  - !ruby/object:Gem::Version
356
- version: 1.78.0
388
+ version: '1.78'
389
+ - - "<"
390
+ - !ruby/object:Gem::Version
391
+ version: '1.87'
357
392
  - !ruby/object:Gem::Dependency
358
393
  name: rubocop-capybara
359
394
  requirement: !ruby/object:Gem::Requirement
@@ -378,16 +413,22 @@ dependencies:
378
413
  name: rubocop-factory_bot
379
414
  requirement: !ruby/object:Gem::Requirement
380
415
  requirements:
381
- - - "~>"
416
+ - - ">="
417
+ - !ruby/object:Gem::Version
418
+ version: '2.27'
419
+ - - "<"
382
420
  - !ruby/object:Gem::Version
383
- version: 2.27.0
421
+ version: '2.29'
384
422
  type: :runtime
385
423
  prerelease: false
386
424
  version_requirements: !ruby/object:Gem::Requirement
387
425
  requirements:
388
- - - "~>"
426
+ - - ">="
427
+ - !ruby/object:Gem::Version
428
+ version: '2.27'
429
+ - - "<"
389
430
  - !ruby/object:Gem::Version
390
- version: 2.27.0
431
+ version: '2.29'
391
432
  - !ruby/object:Gem::Dependency
392
433
  name: rubocop-faker
393
434
  requirement: !ruby/object:Gem::Requirement
@@ -452,22 +493,22 @@ dependencies:
452
493
  name: rubocop-rails
453
494
  requirement: !ruby/object:Gem::Requirement
454
495
  requirements:
455
- - - "~>"
456
- - !ruby/object:Gem::Version
457
- version: 2.32.0
458
496
  - - ">="
459
497
  - !ruby/object:Gem::Version
460
- version: 2.32.0
498
+ version: '2.32'
499
+ - - "<"
500
+ - !ruby/object:Gem::Version
501
+ version: '2.35'
461
502
  type: :runtime
462
503
  prerelease: false
463
504
  version_requirements: !ruby/object:Gem::Requirement
464
505
  requirements:
465
- - - "~>"
466
- - !ruby/object:Gem::Version
467
- version: 2.32.0
468
506
  - - ">="
469
507
  - !ruby/object:Gem::Version
470
- version: 2.32.0
508
+ version: '2.32'
509
+ - - "<"
510
+ - !ruby/object:Gem::Version
511
+ version: '2.35'
471
512
  - !ruby/object:Gem::Dependency
472
513
  name: rubocop-rspec
473
514
  requirement: !ruby/object:Gem::Requirement
@@ -492,16 +533,22 @@ dependencies:
492
533
  name: rubocop-rspec_rails
493
534
  requirement: !ruby/object:Gem::Requirement
494
535
  requirements:
495
- - - "~>"
536
+ - - ">="
537
+ - !ruby/object:Gem::Version
538
+ version: '2.31'
539
+ - - "<"
496
540
  - !ruby/object:Gem::Version
497
- version: 2.31.0
541
+ version: '2.33'
498
542
  type: :runtime
499
543
  prerelease: false
500
544
  version_requirements: !ruby/object:Gem::Requirement
501
545
  requirements:
502
- - - "~>"
546
+ - - ">="
547
+ - !ruby/object:Gem::Version
548
+ version: '2.31'
549
+ - - "<"
503
550
  - !ruby/object:Gem::Version
504
- version: 2.31.0
551
+ version: '2.33'
505
552
  - !ruby/object:Gem::Dependency
506
553
  name: rubocop-rubycw
507
554
  requirement: !ruby/object:Gem::Requirement
@@ -516,6 +563,26 @@ dependencies:
516
563
  - - "~>"
517
564
  - !ruby/object:Gem::Version
518
565
  version: 0.2.0
566
+ - !ruby/object:Gem::Dependency
567
+ name: rubocop-yard
568
+ requirement: !ruby/object:Gem::Requirement
569
+ requirements:
570
+ - - ">="
571
+ - !ruby/object:Gem::Version
572
+ version: '1.0'
573
+ - - "<"
574
+ - !ruby/object:Gem::Version
575
+ version: '1.2'
576
+ type: :runtime
577
+ prerelease: false
578
+ version_requirements: !ruby/object:Gem::Requirement
579
+ requirements:
580
+ - - ">="
581
+ - !ruby/object:Gem::Version
582
+ version: '1.0'
583
+ - - "<"
584
+ - !ruby/object:Gem::Version
585
+ version: '1.2'
519
586
  - !ruby/object:Gem::Dependency
520
587
  name: selenium-webdriver
521
588
  requirement: !ruby/object:Gem::Requirement
@@ -550,14 +617,14 @@ dependencies:
550
617
  requirements:
551
618
  - - "~>"
552
619
  - !ruby/object:Gem::Version
553
- version: 2.1.0
620
+ version: 3.1.0
554
621
  type: :runtime
555
622
  prerelease: false
556
623
  version_requirements: !ruby/object:Gem::Requirement
557
624
  requirements:
558
625
  - - "~>"
559
626
  - !ruby/object:Gem::Version
560
- version: 2.1.0
627
+ version: 3.1.0
561
628
  - !ruby/object:Gem::Dependency
562
629
  name: spring
563
630
  requirement: !ruby/object:Gem::Requirement
@@ -755,6 +822,7 @@ files:
755
822
  - config/locales/zh-TW.yml
756
823
  - config/rubocop/capybara/configuration.yml
757
824
  - config/rubocop/capybara/disabled.yml
825
+ - config/rubocop/decidim-linters/configuration.yml
758
826
  - config/rubocop/disabled.yml
759
827
  - config/rubocop/factory_bot/configuration.yml
760
828
  - config/rubocop/factory_bot/disabled.yml
@@ -770,6 +838,7 @@ files:
770
838
  - config/rubocop/rspec/disabled.yml
771
839
  - config/rubocop/ruby/configuration.yml
772
840
  - config/rubocop/ruby/disabled.yml
841
+ - config/rubocop/yard/configuration.yml
773
842
  - decidim-dev.gemspec
774
843
  - lib/decidim-dev.rb
775
844
  - lib/decidim/api/dummy_resource_type.rb
@@ -802,11 +871,11 @@ files:
802
871
  - lib/decidim/dev/assets/icon.ico
803
872
  - lib/decidim/dev/assets/icon.png
804
873
  - lib/decidim/dev/assets/id.jpg
805
- - lib/decidim/dev/assets/import_participatory_space_private_users.csv
806
- - lib/decidim/dev/assets/import_participatory_space_private_users_invalid_col_sep.csv
807
- - lib/decidim/dev/assets/import_participatory_space_private_users_iso8859-1.csv
808
- - lib/decidim/dev/assets/import_participatory_space_private_users_nok.csv
809
- - lib/decidim/dev/assets/import_participatory_space_private_users_with_bom.csv
874
+ - lib/decidim/dev/assets/import_members.csv
875
+ - lib/decidim/dev/assets/import_members_invalid_col_sep.csv
876
+ - lib/decidim/dev/assets/import_members_iso8859-1.csv
877
+ - lib/decidim/dev/assets/import_members_nok.csv
878
+ - lib/decidim/dev/assets/import_members_with_bom.csv
810
879
  - lib/decidim/dev/assets/import_proposals.csv
811
880
  - lib/decidim/dev/assets/import_proposals.json
812
881
  - lib/decidim/dev/assets/import_proposals.xlsx
@@ -845,6 +914,7 @@ files:
845
914
  - lib/decidim/dev/engine.rb
846
915
  - lib/decidim/dev/needs_development_tools.rb
847
916
  - lib/decidim/dev/railtie.rb
917
+ - lib/decidim/dev/rubocop/cop/decidim/message_antipattern.rb
848
918
  - lib/decidim/dev/test/authorization_shared_examples.rb
849
919
  - lib/decidim/dev/test/base_spec_helper.rb
850
920
  - lib/decidim/dev/test/factories.rb
@@ -905,6 +975,8 @@ files:
905
975
  - lib/decidim/dev/test/spec_helper.rb
906
976
  - lib/decidim/dev/test/w3c_rspec_validators_overrides.rb
907
977
  - lib/decidim/dev/version.rb
978
+ - lib/erb_lint/linters/admin_page_title_linter.rb
979
+ - lib/erb_lint/linters/partial_path_linter.rb
908
980
  - lib/tasks/generators.rake
909
981
  - lib/tasks/lighthouse_report.rake
910
982
  - lib/tasks/locale_checker.rake
@@ -918,7 +990,6 @@ metadata:
918
990
  funding_uri: https://opencollective.com/decidim
919
991
  homepage_uri: https://decidim.org
920
992
  source_code_uri: https://github.com/decidim/decidim
921
- post_install_message:
922
993
  rdoc_options: []
923
994
  require_paths:
924
995
  - lib
@@ -926,15 +997,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
926
997
  requirements:
927
998
  - - "~>"
928
999
  - !ruby/object:Gem::Version
929
- version: 3.3.0
1000
+ version: 3.4.0
930
1001
  required_rubygems_version: !ruby/object:Gem::Requirement
931
1002
  requirements:
932
1003
  - - ">="
933
1004
  - !ruby/object:Gem::Version
934
1005
  version: '0'
935
1006
  requirements: []
936
- rubygems_version: 3.5.11
937
- signing_key:
1007
+ rubygems_version: 3.6.9
938
1008
  specification_version: 4
939
1009
  summary: Decidim dev tools
940
1010
  test_files: []