gourami 2.1.0 → 2.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aa26e60fe628418fe1bf31fc201b9d491b258650306fc2fe08b50787c5425ac2
4
- data.tar.gz: fecc00649a3fa7df0f7acf12ae9baa25cdc8f306caa86819ca4acf523260322b
3
+ metadata.gz: 79b225001fdeb796c9a6224beb9c7c792f37ae0ae7f48be33a2a4ca27f938979
4
+ data.tar.gz: 9fa37364de4f3c646f51f864798029564629a012e16b85877822629d579650a4
5
5
  SHA512:
6
- metadata.gz: 50a29467ecefc99fe47b77775e8ff833c883be91a26fd276eae0f490e46893744bfda7370e653f9f3052312b980a6a81220571501f98c5184b7a9386d7866b35
7
- data.tar.gz: a9c247005a9b661a3d78177eac2a43d39c1438bc43ed63913c3c256308a5d51a7d9a77540ca4c2fbc072c87767fca31eb996394fc0ef182d661220e427a84f20
6
+ metadata.gz: 3b421bf22515e7380abf2a247e74e057ba26eed52d29dc7b2a8f2a13a0f2f72608f0ae6a2c1b0ca5ea4138a4b756b73cb8d9cf234f0bb416237c63c866b06f59
7
+ data.tar.gz: f7dd343730a9503d267f3c39ebb7b769eb62347283a69e5848f0a8e7766b1dcb2da5707eb0b71251eb0f39bdddce47d0fcee212493e811a06618bdbc20be2618
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,37 @@
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
+ sanitized_content = Loofah.html4_fragment(unescaped_content).scrub!(:prune).to_s
31
+ # Unescape again since loofah would turn a & back into &amp;
32
+ CGI.unescapeHTML(sanitized_content).strip
33
+ end
34
+ end
35
+ end
36
+ end
37
+ 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.0".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.0
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-08-31 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.4.21
139
155
  signing_key:
140
156
  specification_version: 4
141
157
  summary: Keep your Routes, Controllers and Models thin.