gourami 2.1.0 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aa26e60fe628418fe1bf31fc201b9d491b258650306fc2fe08b50787c5425ac2
4
- data.tar.gz: fecc00649a3fa7df0f7acf12ae9baa25cdc8f306caa86819ca4acf523260322b
3
+ metadata.gz: a24883a06ab37e3b10cfe74938a8ba46f2f4eb029ff91fef5bd66152fe09cd65
4
+ data.tar.gz: 35072e849b4261a89d0eed5b769b1b37b88eb9c844a4d65a8bd137479e9aa2a7
5
5
  SHA512:
6
- metadata.gz: 50a29467ecefc99fe47b77775e8ff833c883be91a26fd276eae0f490e46893744bfda7370e653f9f3052312b980a6a81220571501f98c5184b7a9386d7866b35
7
- data.tar.gz: a9c247005a9b661a3d78177eac2a43d39c1438bc43ed63913c3c256308a5d51a7d9a77540ca4c2fbc072c87767fca31eb996394fc0ef182d661220e427a84f20
6
+ metadata.gz: 770075df972ce691f10dbd9e380646a942947b8bf8318c78f642973487750a3dc4bd98623d90776344ef3398849905079bc4a15ae75060bd34ff5873c5aae8e8
7
+ data.tar.gz: d0923f40aa9d190f26c7f9d32c1860da10768e099ad7171cdc3e2bb6a4d1331e4546912b24a163f9448a4aa6f75aa538b8ad1324fa70aa0aaa7a5687d49b8671
data/gourami.gemspec CHANGED
@@ -23,6 +23,7 @@ Gem::Specification.new do |spec|
23
23
 
24
24
  spec.add_development_dependency "activesupport", ">= 5.1.7"
25
25
  spec.add_development_dependency "filewatcher", "~> 1.1.0"
26
+ spec.add_development_dependency "loofah", ">= 2.24.0"
26
27
  spec.add_development_dependency "minitest", "~> 5.0"
27
28
  spec.add_development_dependency "pry", "~>0.10"
28
29
  spec.add_development_dependency "rake", "~> 10.0"
@@ -11,7 +11,12 @@ module Gourami
11
11
  # @return [*]
12
12
  def setter_filter(attribute_name, value, options)
13
13
  type = options[:type]
14
- value = send(:"coerce_#{type}", value, options) if type
14
+
15
+ if type
16
+ raise ":coerce_#{type} does not exist. Did you forget to add a plugin for :coerce_#{type}?" unless respond_to?(:"coerce_#{type}")
17
+
18
+ value = send(:"coerce_#{type}", value, options)
19
+ end
15
20
 
16
21
  super(attribute_name, value, options)
17
22
  end
data/lib/gourami/form.rb CHANGED
@@ -88,5 +88,35 @@ module Gourami
88
88
  include Gourami::Validations
89
89
  include Gourami::Coercer
90
90
 
91
+ # Load and apply a plugin.
92
+ # Inspired by the plugin system from the Sequel gem. https://janko.io/the-plugin-system-of-sequel-and-roda/
93
+ #
94
+ # @param name [Symbol, Module] a name to look up under Gourami::Plugins
95
+ # (e.g. :sanitize loads gourami/plugins/sanitize and resolves
96
+ # Gourami::Plugins::Sanitize), or a Module to apply directly.
97
+ def self.plugin(name, *args, &block)
98
+ plugin_module = load_plugin(name)
99
+
100
+ plugin_module.apply(self, *args, &block) if plugin_module.respond_to?(:apply)
101
+
102
+ extend(plugin_module::ClassMethods) if plugin_module.const_defined?(:ClassMethods)
103
+ include(plugin_module::InstanceMethods) if plugin_module.const_defined?(:InstanceMethods)
104
+
105
+ plugin_module.configure(self, *args, &block) if plugin_module.respond_to?(:configure)
106
+ end
107
+
108
+ def self.load_plugin(name)
109
+ return name if name.is_a?(Module)
110
+
111
+ const_name = name.to_s.split("_").map { |part| part[0].upcase + part[1..-1] }.join
112
+
113
+ unless Gourami::Plugins.const_defined?(const_name)
114
+ require "gourami/plugins/#{name}"
115
+ end
116
+
117
+ Gourami::Plugins.const_get(const_name)
118
+ end
119
+ private_class_method :load_plugin
120
+
91
121
  end
92
122
  end
@@ -0,0 +1,43 @@
1
+ # Optional plugin adding a :sanitized_string attribute type that strips dangerous HTML
2
+ # using loofah. Not loaded by default - opt in with `plugin :sanitize`, and add
3
+ # "loofah" to your own application's Gemfile/gemspec.
4
+ #
5
+ # @example
6
+ # class MyForm < Gourami::Form
7
+ # plugin :sanitize
8
+ #
9
+ # attribute :bio, :type => :sanitized_string
10
+ # end
11
+ module Gourami
12
+ module Plugins
13
+ module Sanitize
14
+ # Runs on every `plugin :sanitize` call; require is idempotent so this is safe to repeat.
15
+ def self.apply(_model, *)
16
+ require "loofah"
17
+ end
18
+
19
+ module InstanceMethods
20
+ # @param value [Object]
21
+ # @param options [Hash] forwarded to #coerce_string
22
+ # @return [String, nil]
23
+ def coerce_sanitized_string(value, options = {})
24
+ value = coerce_string(value, options)
25
+ return value if value.nil?
26
+
27
+ # Unescape first so entity-encoded tags (e.g. "&lt;script&gt;") are
28
+ # recognized as real HTML by Loofah instead of passing through as text.
29
+ unescaped_content = CGI.unescapeHTML(value)
30
+
31
+ sanitized_content = if options[:sanitize] == :strict
32
+ Loofah.html4_fragment(unescaped_content).text
33
+ else
34
+ Loofah.html4_fragment(unescaped_content).scrub!(:prune).to_s
35
+ end
36
+
37
+ # Unescape again since loofah would turn a & back into &amp;
38
+ CGI.unescapeHTML(sanitized_content).strip
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,6 @@
1
+ module Gourami
2
+ # Namespace that plugins live under, e.g. Gourami::Plugins::Sanitize is
3
+ # loaded and applied via `plugin :sanitize`.
4
+ module Plugins
5
+ end
6
+ end
@@ -1,3 +1,3 @@
1
1
  module Gourami
2
- VERSION = "2.1.0".freeze
2
+ VERSION = "2.2.1".freeze
3
3
  end
data/lib/gourami.rb CHANGED
@@ -15,5 +15,6 @@ require "gourami/extensions/changes"
15
15
  require "gourami/extensions/resources"
16
16
  require "gourami/form"
17
17
  require "gourami/formatting_constants"
18
+ require "gourami/plugins"
18
19
  require "gourami/validations"
19
20
  require "gourami/version"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gourami
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - TSMMark
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-08-26 00:00:00.000000000 Z
11
+ date: 2026-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: 1.1.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: loofah
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 2.24.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 2.24.0
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: minitest
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -111,6 +125,8 @@ files:
111
125
  - lib/gourami/form.rb
112
126
  - lib/gourami/formatting_constants.rb
113
127
  - lib/gourami/not_watching_changes_error.rb
128
+ - lib/gourami/plugins.rb
129
+ - lib/gourami/plugins/sanitize.rb
114
130
  - lib/gourami/required_attribute_error.rb
115
131
  - lib/gourami/validation_error.rb
116
132
  - lib/gourami/validations.rb
@@ -135,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
135
151
  - !ruby/object:Gem::Version
136
152
  version: '0'
137
153
  requirements: []
138
- rubygems_version: 3.4.12
154
+ rubygems_version: 3.2.3
139
155
  signing_key:
140
156
  specification_version: 4
141
157
  summary: Keep your Routes, Controllers and Models thin.