is_it_spam_rails 2.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e52a3a701411ca162d602b08ef9672ef5e4fadcb8852021af50777a555a76b82
4
+ data.tar.gz: 90f33118154bf77776ba35778e662512ae3c6ca8569a38c7bc1d5c20f36591ca
5
+ SHA512:
6
+ metadata.gz: 3c6060a1e7d1dff377d55e699d71cc662dc447713e914b6da8e5cfe403ac4c2788f0438e8ae49641b6a487ae77049dc5320de4677eae0e22451958b6dec72978
7
+ data.tar.gz: 0f57ee01f7cff1f90c590c728a1de6faa44c83126b637f144f905bb19166dfe2bdcbcb1f76781673ce21c80ee25efcb687173cb944d0f376cf9471181c0b3ccc
data/CHANGELOG.md ADDED
@@ -0,0 +1,53 @@
1
+ ## [Unreleased]
2
+
3
+ ## [2.0.0] - TBD
4
+
5
+ ### ⚠️ Breaking Changes
6
+ - **IP tracking is now ON by default** - The gem automatically captures and sends end user IP addresses (`request.remote_ip`) to the API
7
+ - End user IPs are tracked for spam detection and blocking across all apps
8
+ - To disable: set `config.track_end_user_ip = false` in your initializer
9
+ - Review your privacy policy before upgrading - IP addresses are personal data under GDPR
10
+ - Existing apps upgrading from 0.1.x will start tracking IPs immediately upon deployment
11
+
12
+ ### Added
13
+ - **End user IP tracking** - Capture the actual IP address of the person filling out your form
14
+ - New configuration option: `track_end_user_ip` (default: true)
15
+ - Sends `end_user_ip` parameter to API for IP-based spam blocking
16
+ - Enables blocking malicious users across your entire hosting system
17
+ - IP tracking can be disabled per-app via configuration
18
+ - Backward compatible with is-it-spam.com API (falls back to server IP if not provided)
19
+
20
+ ### Changed
21
+ - `Client#check_spam` now accepts optional `end_user_ip:` parameter
22
+ - `IsItSpamRails.check_spam` module method now accepts optional `end_user_ip:` parameter
23
+ - Controller extension automatically captures `request.remote_ip` when tracking is enabled
24
+
25
+ ### Privacy & GDPR
26
+ - End user IP addresses are now sent to is-it-spam.com by default
27
+ - IPs are stored in spam check logs for spam detection and analytics
28
+ - Legal basis: Legitimate interest (spam prevention)
29
+ - Users can opt-out with `config.track_end_user_ip = false`
30
+ - App owners should update privacy policies to disclose IP tracking
31
+
32
+ ## [0.1.0] - 2025-06-14
33
+
34
+ ### Added
35
+ - Rails integration gem for is-it-spam.com anti-spam service
36
+ - `is_it_spam` controller method with before_action hooks
37
+ - Manual spam handling mode (returns @spam_check_result for custom handling)
38
+ - Automatic spam handling mode with `on_spam` configuration for redirects
39
+ - Rails configuration through credentials and environment variables
40
+ - HTTP client with comprehensive error handling and rate limiting
41
+ - Rails Railtie for automatic controller extension inclusion
42
+ - Rails generator for easy setup
43
+ - Comprehensive test suite with Rails integration testing
44
+ - Support for nested parameter extraction (contact, commission, inquiry forms)
45
+ - Configurable API timeouts and base URL
46
+ - Rails logger integration for error reporting
47
+
48
+ ### Features
49
+ - **Controller Integration**: Simple `is_it_spam only: [:create], on_spam: { redirect_to: root_path, notice: 'message' }` API
50
+ - **Flexible Configuration**: Rails credentials, environment variables, or manual configuration
51
+ - **Error Handling**: Graceful degradation with proper logging on API failures
52
+ - **Rails Native**: Uses ActiveSupport::Concern and Rails conventions
53
+ - **Production Ready**: Comprehensive error handling and logging
data/CLAUDE.md ADDED
@@ -0,0 +1,59 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ This is a Ruby gem that provides Rails integration for the is-it-spam.com anti-spam service. The gem adds `check_spam` before_action hooks to Rails controllers for automatic spam detection.
8
+
9
+ ## Architecture
10
+
11
+ The gem follows a modular architecture:
12
+
13
+ - **Core API Client** (`lib/is_it_spam_rails/client.rb`) - HTTP client for is-it-spam.com API
14
+ - **Configuration** (`lib/is_it_spam_rails/configuration.rb`) - Handles API credentials and settings
15
+ - **Rails Integration** (`lib/is_it_spam_rails/railtie.rb`) - Integrates with Rails through Railtie
16
+ - **Controller Extension** (`lib/is_it_spam_rails/controller_extension.rb`) - Adds `check_spam` method to controllers
17
+ - **Spam Checker** (`lib/is_it_spam_rails/spam_checker.rb`) - Core spam checking logic
18
+ - **Rails Generator** (`lib/generators/is_it_spam_rails/install_generator.rb`) - Creates initializer file
19
+
20
+ ## Commands
21
+
22
+ ### Testing
23
+ ```bash
24
+ rake test # Run all tests
25
+ rake test test/test_client.rb # Run specific test file
26
+ ```
27
+
28
+ ### Development
29
+ ```bash
30
+ bin/setup # Install dependencies
31
+ bin/console # Start interactive console
32
+ bundle install # Install gem dependencies
33
+ ```
34
+
35
+ ### Gem Tasks
36
+ ```bash
37
+ rake build # Build the gem
38
+ rake install # Install locally
39
+ rake release # Release to RubyGems
40
+ ```
41
+
42
+ ### Rails Integration Testing
43
+ ```bash
44
+ bin/rails is_it_spam:test_connection # Test API connection
45
+ bin/rails is_it_spam:test_spam_check # Test spam detection
46
+ bin/rails is_it_spam:config # Show configuration
47
+ bin/rails is_it_spam:install # Install initializer
48
+ ```
49
+
50
+ ## Development Notes
51
+
52
+ - Uses Minitest for testing with WebMock for HTTP stubbing
53
+ - All methods should include YARD documentation
54
+ - Classes should have description comments
55
+ - Environment variables: `IS_IT_SPAM_API_KEY`, `IS_IT_SPAM_API_SECRET`, `IS_IT_SPAM_BASE_URL`
56
+ - Rails credentials path: `is_it_spam_rails.api_key`, `is_it_spam_rails.api_secret`
57
+ - Gem supports Rails 6.0+ and Ruby 3.1+
58
+ - Uses HTTParty for HTTP requests
59
+ - Configuration auto-loads from Rails credentials or environment variables
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Benjamin Deutscher
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,253 @@
1
+ # IsItSpamRails
2
+
3
+ Rails integration gem for [is-it-spam.com](https://is-it-spam.com) anti-spam service. Provides easy-to-use before_action hooks for automatic spam detection in your Rails controllers.
4
+
5
+ ## Installation
6
+
7
+ Add the gem to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'is_it_spam_rails'
11
+ ```
12
+
13
+ Then execute:
14
+
15
+ ```bash
16
+ $ bundle install
17
+ ```
18
+
19
+ Run the installer to create the initializer:
20
+
21
+ ```bash
22
+ $ bin/rails is_it_spam:install
23
+ ```
24
+
25
+ ## Configuration
26
+
27
+ Configure your API credentials in `config/initializers/is_it_spam_rails.rb`:
28
+
29
+ ### Option 1: Rails Credentials (Recommended)
30
+
31
+ ```bash
32
+ $ rails credentials:edit
33
+ ```
34
+
35
+ Add your credentials:
36
+
37
+ ```yaml
38
+ is_it_spam_rails:
39
+ api_key: your_api_key_here
40
+ api_secret: your_api_secret_here
41
+ base_url: https://is-it-spam.com # optional
42
+ ```
43
+
44
+ Then in your initializer:
45
+
46
+ ```ruby
47
+ IsItSpamRails.configure do |config|
48
+ config.api_key = Rails.application.credentials.is_it_spam_rails[:api_key]
49
+ config.api_secret = Rails.application.credentials.is_it_spam_rails[:api_secret]
50
+ end
51
+ ```
52
+
53
+ ### Option 2: Environment Variables
54
+
55
+ Set environment variables:
56
+
57
+ ```bash
58
+ export IS_IT_SPAM_API_KEY=your_api_key_here
59
+ export IS_IT_SPAM_API_SECRET=your_api_secret_here
60
+ export IS_IT_SPAM_BASE_URL=https://is-it-spam.com # optional
61
+ ```
62
+
63
+ The gem will automatically use these if no explicit configuration is provided.
64
+
65
+ ### End User IP Tracking
66
+
67
+ By default, the gem tracks the IP address of the person filling out your form (`request.remote_ip`) and sends it to the is-it-spam.com API for IP-based spam blocking.
68
+
69
+ **Privacy Considerations:**
70
+ - End user IPs are stored in spam check logs for analytics and spam detection
71
+ - IPs can be blocked at system, user, or app level
72
+ - Consider your privacy policy and GDPR requirements when deploying this feature
73
+
74
+ **Configuration:**
75
+
76
+ ```ruby
77
+ IsItSpamRails.configure do |config|
78
+ config.api_key = "your_api_key"
79
+ config.api_secret = "your_api_secret"
80
+ config.track_end_user_ip = true # Default: true
81
+ end
82
+ ```
83
+
84
+ **To disable IP tracking:**
85
+
86
+ ```ruby
87
+ IsItSpamRails.configure do |config|
88
+ config.api_key = "your_api_key"
89
+ config.api_secret = "your_api_secret"
90
+ config.track_end_user_ip = false # Disable end user IP tracking
91
+ end
92
+ ```
93
+
94
+ When disabled, the API will still receive the server's IP address (for rate limiting purposes), but individual form submitters won't be tracked.
95
+
96
+ **Breaking Change:** Starting with version 2.0.0, IP tracking is **ON by default**. If you upgrade from an earlier version, end user IPs will automatically be tracked unless you explicitly disable it. Review your privacy policy before upgrading.
97
+
98
+ ## Usage
99
+
100
+ ### Basic Usage
101
+
102
+ Add spam checking to your controllers with the `is_it_spam` method. By default, the gem sets `@spam_check_result` and lets you handle spam detection manually:
103
+
104
+ ```ruby
105
+ class ContactController < ApplicationController
106
+ is_it_spam only: [:create]
107
+
108
+ def create
109
+ # Check if spam was detected
110
+ if @spam_check_result&.spam?
111
+ # Handle spam manually
112
+ flash[:alert] = "Your submission appears to be spam"
113
+ redirect_to root_path
114
+ return
115
+ end
116
+
117
+ # Process legitimate submissions
118
+ confidence = @spam_check_result&.confidence_score
119
+ Rails.logger.info "Legitimate submission with #{(confidence * 100).round(1)}% confidence"
120
+ # ...
121
+ end
122
+ end
123
+ ```
124
+
125
+ ### Automatic Spam Handling
126
+
127
+ For automatic spam handling, use the `on_spam` configuration to redirect spam submissions:
128
+
129
+ ```ruby
130
+ class ContactController < ApplicationController
131
+ is_it_spam only: [:create], on_spam: {
132
+ redirect_to: root_path,
133
+ notice: 'Thank you for your message'
134
+ }
135
+
136
+ def create
137
+ # This action only executes for legitimate (non-spam) submissions
138
+ # @spam_check_result is available and will never be spam
139
+
140
+ # Process the legitimate submission
141
+ # ...
142
+ end
143
+ end
144
+ ```
145
+
146
+ ### Configuration Options
147
+
148
+ The `on_spam` hash accepts the following options:
149
+
150
+ ```ruby
151
+ class ContactController < ApplicationController
152
+ is_it_spam only: [:create], on_spam: {
153
+ redirect_to: root_path, # Path to redirect on spam detection
154
+ notice: 'Thank you for contacting us' # Flash notice message
155
+ }
156
+ end
157
+ ```
158
+
159
+ You can also use `alert` instead of `notice`:
160
+
161
+ ```ruby
162
+ class ContactController < ApplicationController
163
+ is_it_spam only: [:create], on_spam: {
164
+ redirect_to: root_path,
165
+ alert: 'There was an issue with your submission'
166
+ }
167
+ end
168
+ ```
169
+
170
+ ### Dynamic Redirect Paths
171
+
172
+ Use route helpers or callable objects for dynamic paths:
173
+
174
+ ```ruby
175
+ class ContactController < ApplicationController
176
+ is_it_spam only: [:create], on_spam: {
177
+ redirect_to: Rails.application.routes.url_helpers.root_path,
178
+ notice: I18n.t('contact.success')
179
+ }
180
+ end
181
+ ```
182
+
183
+ ### Parameter Detection
184
+
185
+ The gem automatically detects form parameters from common nested keys:
186
+ - `:commission`, `:contact`, `:inquiry`, `:message`, `:form`
187
+ - Maps `name`, `email`, and `message` fields
188
+ - Supports `first_name`/`last_name` combination for name field
189
+
190
+ ## Testing and Debugging
191
+
192
+ The gem includes rake tasks for testing your configuration:
193
+
194
+ ```bash
195
+ # Test API connection
196
+ $ bin/rails is_it_spam:test_connection
197
+
198
+ # Test spam detection with sample data
199
+ $ bin/rails is_it_spam:test_spam_check
200
+
201
+ # Show current configuration
202
+ $ bin/rails is_it_spam:config
203
+ ```
204
+
205
+ ## API
206
+
207
+ ### Direct API Usage
208
+
209
+ You can also use the API directly without the before_action:
210
+
211
+ ```ruby
212
+ result = IsItSpamRails.check_spam(
213
+ name: "John Doe",
214
+ email: "john@example.com",
215
+ message: "Your message here",
216
+ custom_fields: { company: "Acme Corp" }
217
+ )
218
+
219
+ if result.spam?
220
+ puts "Spam detected: #{result.spam_reasons.join(', ')}"
221
+ puts "Confidence: #{(result.confidence_score * 100).round(1)}%"
222
+ else
223
+ puts "Message appears legitimate"
224
+ end
225
+ ```
226
+
227
+ ### Health Check
228
+
229
+ Check if the API service is available:
230
+
231
+ ```ruby
232
+ if IsItSpamRails.health_check
233
+ puts "API is healthy"
234
+ else
235
+ puts "API is not responding"
236
+ end
237
+ ```
238
+
239
+ ## Error Handling
240
+
241
+ The gem is designed to fail gracefully. If the API is unavailable, rate limited, or returns errors, your application will continue to function normally. Errors are logged but don't block legitimate users.
242
+
243
+ ## Development
244
+
245
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests.
246
+
247
+ ## Contributing
248
+
249
+ Bug reports and pull requests are welcome on GitHub at https://github.com/its-benjamin-deutscher/is-it-spam.com.
250
+
251
+ ## License
252
+
253
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ Minitest::TestTask.create
7
+
8
+ task default: :test
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module IsItSpamRails
6
+ module Generators
7
+ # Rails generator for installing IsItSpamRails
8
+ #
9
+ # Creates an initializer file with configuration template
10
+ class InstallGenerator < Rails::Generators::Base
11
+ desc "Install IsItSpamRails by creating an initializer"
12
+
13
+ # Define source location for templates
14
+ source_root File.expand_path("templates", __dir__)
15
+
16
+ # Create the initializer file
17
+ #
18
+ # @return [void]
19
+ def create_initializer
20
+ template "initializer.rb", "config/initializers/is_it_spam_rails.rb"
21
+ end
22
+
23
+ # Display installation instructions
24
+ #
25
+ # @return [void]
26
+ def show_instructions
27
+ say ""
28
+ say "IsItSpamRails has been installed!", :green
29
+ say ""
30
+ say "Next steps:"
31
+ say "1. Configure your API credentials in config/initializers/is_it_spam_rails.rb"
32
+ say "2. Add your credentials to Rails credentials or environment variables"
33
+ say "3. Use is_it_spam in your controllers as a before_action"
34
+ say ""
35
+ say "Example usage in a controller:"
36
+ say " class ContactController < ApplicationController"
37
+ say " is_it_spam only: [:create], on_spam: {"
38
+ say " redirect_to: root_path,"
39
+ say " notice: 'Thank you for your message'"
40
+ say " }"
41
+ say " end"
42
+ say ""
43
+ say "For more information, visit: https://is-it-spam.com/docs"
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Configuration for IsItSpamRails gem
4
+ #
5
+ # This initializer configures the connection to the is-it-spam.com API
6
+ # for automated spam detection in your Rails application.
7
+ #
8
+ # For more information, visit: https://is-it-spam.com/docs
9
+
10
+ IsItSpamRails.configure do |config|
11
+ # Your API credentials from is-it-spam.com
12
+ # You can get these from your dashboard at https://is-it-spam.com/dashboard
13
+
14
+ # Option 1: Use Rails credentials (recommended)
15
+ # Run: rails credentials:edit
16
+ # Add:
17
+ # is_it_spam_rails:
18
+ # api_key: your_api_key_here
19
+ # api_secret: your_api_secret_here
20
+ # base_url: https://is-it-spam.com # optional, defaults to production
21
+ #
22
+ # Then uncomment these lines:
23
+ # config.api_key = Rails.application.credentials.is_it_spam_rails[:api_key]
24
+ # config.api_secret = Rails.application.credentials.is_it_spam_rails[:api_secret]
25
+ # config.base_url = Rails.application.credentials.is_it_spam_rails[:base_url] # optional
26
+
27
+ # Option 2: Use environment variables
28
+ # Set these in your environment:
29
+ # IS_IT_SPAM_API_KEY=your_api_key_here
30
+ # IS_IT_SPAM_API_SECRET=your_api_secret_here
31
+ # IS_IT_SPAM_BASE_URL=https://is-it-spam.com # optional
32
+ #
33
+ # Then uncomment these lines:
34
+ # config.api_key = ENV["IS_IT_SPAM_API_KEY"]
35
+ # config.api_secret = ENV["IS_IT_SPAM_API_SECRET"]
36
+ # config.base_url = ENV["IS_IT_SPAM_BASE_URL"] # optional
37
+
38
+ # Option 3: Direct configuration (not recommended for production)
39
+ # config.api_key = "your_api_key_here"
40
+ # config.api_secret = "your_api_secret_here"
41
+
42
+ # Optional configuration
43
+ # config.base_url = "https://is-it-spam.com" # API base URL
44
+ # config.timeout = 30 # Request timeout in seconds
45
+ # config.logger = Rails.logger # Logger for debugging
46
+ end
47
+
48
+ # Usage in controllers:
49
+ #
50
+ # class ContactController < ApplicationController
51
+ # # Basic usage - detects spam and redirects with notice
52
+ # check_spam only: :create
53
+ #
54
+ # # Custom configuration
55
+ # check_spam only: :create,
56
+ # redirect_path: root_path,
57
+ # notice: "Thank you for your message"
58
+ #
59
+ # # With custom parameter mapping
60
+ # check_spam only: :create,
61
+ # param_key: :contact_form, # if your params are nested under contact_form
62
+ # custom_fields: { # map additional fields
63
+ # company: :company_name,
64
+ # phone: :phone_number
65
+ # }
66
+ #
67
+ # def create
68
+ # # Your normal controller logic
69
+ # # Spam checking happens automatically before this action
70
+ #
71
+ # # You can access the spam check result if needed:
72
+ # # @spam_check_result.spam?
73
+ # # @spam_check_result.confidence_score
74
+ # # @spam_check_result.spam_reasons
75
+ # end
76
+ # end
@@ -0,0 +1,274 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "httparty"
4
+
5
+ module IsItSpamRails
6
+ # Main client class for interacting with the Is It Spam API
7
+ #
8
+ # Provides methods for checking spam and communicating with the API
9
+ class Client
10
+ include HTTParty
11
+
12
+ # API version
13
+ API_VERSION = "v1"
14
+ # Default timeout for requests
15
+ DEFAULT_TIMEOUT = 30
16
+
17
+ # Initialize client with credentials
18
+ #
19
+ # @param api_key [String] Your API key from Is It Spam
20
+ # @param api_secret [String] Your API secret from Is It Spam
21
+ # @param base_url [String] Base URL for the API (default: https://is-it-spam.com)
22
+ # @param timeout [Integer] Request timeout in seconds (default: 30)
23
+ # @raise [ConfigurationError] When credentials are missing
24
+ def initialize(api_key:, api_secret:, base_url: "https://is-it-spam.com", timeout: DEFAULT_TIMEOUT)
25
+ @api_key = api_key
26
+ @api_secret = api_secret
27
+ @base_url = base_url.chomp("/")
28
+ @timeout = timeout
29
+
30
+ validate_credentials!
31
+
32
+ self.class.base_uri @base_url
33
+ self.class.default_timeout @timeout
34
+ end
35
+
36
+ # Check if the provided content is spam
37
+ #
38
+ # @param name [String] Name from the contact form
39
+ # @param email [String] Email address from the contact form
40
+ # @param message [String] Message content from the contact form
41
+ # @param custom_fields [Hash] Additional custom fields to check (optional)
42
+ # @param end_user_ip [String, nil] IP address of the end user filling the form (optional)
43
+ # @return [SpamCheckResult] The result of the spam check
44
+ # @raise [ValidationError] When required parameters are missing or invalid
45
+ # @raise [ApiError] When the API returns an error
46
+ # @raise [RateLimitError] When rate limits are exceeded
47
+ def check_spam(name:, email:, message:, custom_fields: {}, end_user_ip: nil)
48
+ validate_required_params(name: name, email: email, message: message)
49
+
50
+ payload = {
51
+ spam_check: {
52
+ name: name,
53
+ email: email,
54
+ message: message,
55
+ additional_fields: custom_fields
56
+ }
57
+ }
58
+
59
+ # Only include end_user_ip if tracking is enabled and IP is provided
60
+ if IsItSpamRails.configuration.track_end_user_ip && end_user_ip.present?
61
+ payload[:spam_check][:end_user_ip] = end_user_ip
62
+ end
63
+
64
+ response = make_request(:post, "/api/#{API_VERSION}/spam_checks", body: payload.to_json)
65
+ SpamCheckResult.new(response.parsed_response)
66
+ end
67
+
68
+ # Check the health of the API service
69
+ #
70
+ # @return [Boolean] true if the service is healthy
71
+ # @raise [ApiError] When the API is unavailable
72
+ def health_check
73
+ make_request(:get, "/up")
74
+ true
75
+ rescue ApiError => e
76
+ raise e unless e.status_code == 503
77
+ false
78
+ end
79
+
80
+ private
81
+
82
+ # Validate that API credentials are present
83
+ #
84
+ # @raise [ConfigurationError] When credentials are missing
85
+ def validate_credentials!
86
+ raise ConfigurationError, "API key is required" if @api_key.nil? || @api_key.empty?
87
+ raise ConfigurationError, "API secret is required" if @api_secret.nil? || @api_secret.empty?
88
+ end
89
+
90
+ # Validate required parameters for spam checking
91
+ #
92
+ # @param name [String] Name parameter
93
+ # @param email [String] Email parameter
94
+ # @param message [String] Message parameter
95
+ # @raise [ValidationError] When parameters are invalid
96
+ def validate_required_params(name:, email:, message:)
97
+ errors = {}
98
+
99
+ errors[:name] = ["can't be blank"] if name.nil? || name.empty?
100
+ errors[:email] = ["can't be blank"] if email.nil? || email.empty?
101
+ errors[:message] = ["can't be blank"] if message.nil? || message.empty?
102
+
103
+ # Basic email format validation
104
+ if email && !email.match?(/\A[^@\s]+@[^@\s]+\z/)
105
+ errors[:email] = (errors[:email] || []) << "is not a valid email address"
106
+ end
107
+
108
+ unless errors.empty?
109
+ raise ValidationError.new("Validation failed", errors: errors)
110
+ end
111
+ end
112
+
113
+ # Make an HTTP request to the API
114
+ #
115
+ # @param method [Symbol] HTTP method (:get, :post, etc.)
116
+ # @param path [String] API endpoint path
117
+ # @param options [Hash] Additional request options
118
+ # @return [HTTParty::Response] HTTP response object
119
+ # @raise [ApiError, RateLimitError, ValidationError] Based on response
120
+ def make_request(method, path, **options)
121
+ request_options = {
122
+ headers: {
123
+ "Content-Type" => "application/json",
124
+ "X-API-Key" => @api_key,
125
+ "X-API-Secret" => @api_secret,
126
+ "User-Agent" => "IsItSpam Rails Gem #{IsItSpamRails::VERSION}"
127
+ }
128
+ }.merge(options)
129
+
130
+ response = self.class.send(method, path, request_options)
131
+ handle_response(response)
132
+ end
133
+
134
+ # Handle API response and raise appropriate errors
135
+ #
136
+ # @param response [HTTParty::Response] HTTP response object
137
+ # @return [HTTParty::Response] Response for successful requests
138
+ # @raise [ApiError, RateLimitError, ValidationError] Based on status code
139
+ def handle_response(response)
140
+ case response.code
141
+ when 200..299
142
+ response
143
+ when 400
144
+ handle_error_response(response, ApiError)
145
+ when 401
146
+ handle_error_response(response, ApiError)
147
+ when 404
148
+ raise ApiError.new("Endpoint not found", status_code: 404, response_body: response.body)
149
+ when 422
150
+ handle_validation_error(response)
151
+ when 429
152
+ handle_error_response(response, RateLimitError)
153
+ when 500..599
154
+ handle_error_response(response, ApiError)
155
+ else
156
+ raise ApiError.new("Unexpected response code: #{response.code}",
157
+ status_code: response.code,
158
+ response_body: response.body)
159
+ end
160
+ end
161
+
162
+ # Handle error responses with JSON body
163
+ #
164
+ # @param response [HTTParty::Response] HTTP response object
165
+ # @param error_class [Class] Error class to raise
166
+ # @raise [ApiError, RateLimitError] Specified error class
167
+ def handle_error_response(response, error_class)
168
+ begin
169
+ error_data = response.parsed_response
170
+ message = error_data["error"] || "API request failed"
171
+ rescue
172
+ message = "API request failed"
173
+ end
174
+
175
+ raise error_class.new(message, status_code: response.code, response_body: response.body)
176
+ end
177
+
178
+ # Handle validation error responses
179
+ #
180
+ # @param response [HTTParty::Response] HTTP response object
181
+ # @raise [ValidationError] With field-specific errors
182
+ def handle_validation_error(response)
183
+ begin
184
+ error_data = response.parsed_response
185
+ message = error_data["error"] || "Validation failed"
186
+ errors = error_data["errors"] || {}
187
+ rescue
188
+ message = "Validation failed"
189
+ errors = {}
190
+ end
191
+
192
+ raise ValidationError.new(message, errors: errors, status_code: 422, response_body: response.body)
193
+ end
194
+ end
195
+
196
+ # Represents the result of a spam check operation
197
+ #
198
+ # Provides convenient methods for accessing spam detection results
199
+ class SpamCheckResult
200
+ # @return [Boolean] Whether content was identified as spam
201
+ attr_reader :spam
202
+ # @return [Float] Confidence score between 0.0 and 1.0
203
+ attr_reader :confidence
204
+ # @return [Array<String>] Reasons why content was flagged as spam
205
+ attr_reader :reasons
206
+
207
+ # Initialize spam check result
208
+ #
209
+ # @param data [Hash] Response data from the API
210
+ def initialize(data)
211
+ @spam = data["spam"]
212
+ @confidence = data["confidence"].to_f
213
+ @reasons = (data["reasons"] || []).dup.freeze
214
+ end
215
+
216
+ # Check if content was identified as spam
217
+ #
218
+ # @return [Boolean] true if content was identified as spam
219
+ def spam?
220
+ @spam
221
+ end
222
+
223
+ # Check if content appears legitimate
224
+ #
225
+ # @return [Boolean] true if content appears legitimate
226
+ def legitimate?
227
+ !spam?
228
+ end
229
+
230
+ # Get confidence score
231
+ #
232
+ # @return [Float] Confidence score between 0.0 and 1.0
233
+ def confidence_score
234
+ @confidence
235
+ end
236
+
237
+ # Get reasons for spam detection
238
+ #
239
+ # @return [Array<String>] Reasons why content was flagged as spam
240
+ def spam_reasons
241
+ @reasons
242
+ end
243
+
244
+ # Get human-readable summary of the result
245
+ #
246
+ # @return [String] Human-readable summary of the result
247
+ def summary
248
+ if spam?
249
+ "Spam detected (#{(@confidence * 100).round(1)}% confidence): #{@reasons.join(', ')}"
250
+ else
251
+ "Content appears legitimate (#{(@confidence * 100).round(1)}% confidence)"
252
+ end
253
+ end
254
+
255
+ # Convert result to hash
256
+ #
257
+ # @return [Hash] Hash representation of the result
258
+ def to_h
259
+ {
260
+ spam: @spam,
261
+ confidence: @confidence,
262
+ reasons: @reasons
263
+ }
264
+ end
265
+
266
+ # Convert result to JSON
267
+ #
268
+ # @param args [Array] Arguments passed to to_json
269
+ # @return [String] JSON representation of the result
270
+ def to_json(*args)
271
+ to_h.to_json(*args)
272
+ end
273
+ end
274
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IsItSpamRails
4
+ # Configuration class for IsItSpamRails gem
5
+ #
6
+ # Handles global configuration settings for the Rails integration
7
+ class Configuration
8
+ # @return [String, nil] API key for authentication
9
+ attr_accessor :api_key
10
+ # @return [String, nil] API secret for authentication
11
+ attr_accessor :api_secret
12
+ # @return [String] Base URL for the API
13
+ attr_accessor :base_url
14
+ # @return [Integer] Request timeout in seconds
15
+ attr_accessor :timeout
16
+ # @return [Logger, nil] Logger instance for debugging
17
+ attr_accessor :logger
18
+ # @return [Boolean] Whether to track end user IP addresses (default: true)
19
+ attr_accessor :track_end_user_ip
20
+
21
+ # Initialize configuration with default values
22
+ def initialize
23
+ @base_url = "https://is-it-spam.com"
24
+ @timeout = 30
25
+ @logger = rails_logger
26
+ @track_end_user_ip = true
27
+ end
28
+
29
+ # Get configured client instance
30
+ #
31
+ # @return [Client] The configured client
32
+ # @raise [ConfigurationError] When required configuration is missing
33
+ def client
34
+ @client ||= Client.new(
35
+ api_key: api_key,
36
+ api_secret: api_secret,
37
+ base_url: base_url,
38
+ timeout: timeout
39
+ )
40
+ end
41
+
42
+ # Reset the client (useful for testing or credential changes)
43
+ def reset_client!
44
+ @client = nil
45
+ end
46
+
47
+ # Check if configuration is valid
48
+ #
49
+ # @return [Boolean] true if configuration has required values
50
+ def valid?
51
+ !api_key.nil? && !api_key.empty? &&
52
+ !api_secret.nil? && !api_secret.empty?
53
+ end
54
+
55
+ # Validate configuration and raise error if invalid
56
+ #
57
+ # @raise [ConfigurationError] When configuration is invalid
58
+ def validate!
59
+ raise ConfigurationError, "API key is required" if api_key.nil? || api_key.empty?
60
+ raise ConfigurationError, "API secret is required" if api_secret.nil? || api_secret.empty?
61
+ end
62
+
63
+ private
64
+
65
+ # Get Rails logger if available, nil otherwise
66
+ #
67
+ # @return [Logger, nil] Rails logger or nil
68
+ def rails_logger
69
+ return nil unless defined?(Rails)
70
+ return nil unless Rails.respond_to?(:logger)
71
+ Rails.logger
72
+ rescue
73
+ nil
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IsItSpamRails
4
+ # Controller extension providing spam checking functionality
5
+ #
6
+ # Adds the `is_it_spam` class method to Rails controllers for automatic spam detection
7
+ module ControllerExtension
8
+ extend ActiveSupport::Concern
9
+
10
+ module ClassMethods
11
+ # Add spam checking as a before_action
12
+ #
13
+ # @param options [Hash] Configuration options
14
+ # @option options [Hash] :on_spam Options for handling spam detection
15
+ # @option options [String, Proc] :on_spam[:redirect_to] Path to redirect to when spam is detected
16
+ # @option options [String] :on_spam[:notice] Flash notice message to display
17
+ # @option options [String] :on_spam[:alert] Flash alert message to display
18
+ # @option options [Symbol, String] :form_param_name Name of the nested parameter containing form data
19
+ def is_it_spam(options = {})
20
+ on_spam_options = options.delete(:on_spam) || {}
21
+ form_param_name = options.delete(:form_param_name)
22
+
23
+ before_action(options) do
24
+ check_for_spam(on_spam_options, form_param_name)
25
+ end
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ # Check for spam and handle accordingly
32
+ #
33
+ # @param on_spam_options [Hash] Options for spam handling
34
+ # @param form_param_name [Symbol, String, nil] Name of the nested parameter containing form data
35
+ # @return [void]
36
+ def check_for_spam(on_spam_options = {}, form_param_name = nil)
37
+ # Extract form parameters - try custom parameter name first if provided
38
+ form_params = extract_form_params(form_param_name)
39
+
40
+ # Skip if essential parameters are blank
41
+ return unless form_params[:name].present? && form_params[:email].present? && form_params[:message].present?
42
+
43
+ # Capture end user IP if tracking is enabled and request is available
44
+ end_user_ip = if IsItSpamRails.configuration.track_end_user_ip && respond_to?(:request) && request.respond_to?(:remote_ip)
45
+ request.remote_ip
46
+ else
47
+ nil
48
+ end
49
+
50
+ begin
51
+ @spam_check_result = IsItSpamRails.check_spam(
52
+ name: form_params[:name],
53
+ email: form_params[:email],
54
+ message: form_params[:message],
55
+ custom_fields: {},
56
+ end_user_ip: end_user_ip
57
+ )
58
+
59
+ if @spam_check_result&.spam? && on_spam_options.any?
60
+ handle_spam_detection(on_spam_options)
61
+ end
62
+ rescue IsItSpamRails::ValidationError => e
63
+ Rails.logger&.warn("Spam check validation failed: #{e.message}")
64
+ rescue IsItSpamRails::RateLimitError => e
65
+ Rails.logger&.warn("Spam check rate limit exceeded: #{e.message}")
66
+ rescue IsItSpamRails::ApiError => e
67
+ Rails.logger&.error("Spam check API error: #{e.message}")
68
+ rescue StandardError => e
69
+ Rails.logger&.error("Spam check unexpected error: #{e.message}")
70
+ end
71
+ end
72
+
73
+ # Extract form parameters from nested params
74
+ #
75
+ # @param form_param_name [Symbol, String, nil] Name of the nested parameter containing form data
76
+ # @return [Hash] Extracted form parameters
77
+ def extract_form_params(form_param_name = nil)
78
+ # First try custom form parameter name if provided
79
+ if form_param_name && params[form_param_name.to_sym].is_a?(ActionController::Parameters)
80
+ nested_params = params[form_param_name.to_sym]
81
+ return {
82
+ name: nested_params[:name] || nested_params[:first_name] || "#{nested_params[:first_name]} #{nested_params[:last_name]}".strip,
83
+ email: nested_params[:email],
84
+ message: nested_params[:message] || nested_params[:body] || nested_params[:content]
85
+ }
86
+ end
87
+
88
+ # Try common form parameter keys for backward compatibility
89
+ common_keys = [:commission, :contact, :inquiry, :message, :form]
90
+
91
+ # Try nested parameters
92
+ common_keys.each do |key|
93
+ if params[key].is_a?(ActionController::Parameters)
94
+ nested_params = params[key]
95
+ return {
96
+ name: nested_params[:name] || nested_params[:first_name] || "#{nested_params[:first_name]} #{nested_params[:last_name]}".strip,
97
+ email: nested_params[:email],
98
+ message: nested_params[:message] || nested_params[:body] || nested_params[:content]
99
+ }
100
+ end
101
+ end
102
+
103
+ # Fallback to direct parameter access
104
+ {
105
+ name: params[:name] || params[:first_name] || "#{params[:first_name]} #{params[:last_name]}".strip,
106
+ email: params[:email],
107
+ message: params[:message] || params[:body] || params[:content]
108
+ }
109
+ end
110
+
111
+ # Handle spam detection by redirecting with flash message
112
+ #
113
+ # @param options [Hash] Spam handling options
114
+ # @return [void]
115
+ def handle_spam_detection(options = {})
116
+ redirect_path = options[:redirect_to] || root_path
117
+ redirect_path = redirect_path.call if redirect_path.respond_to?(:call)
118
+
119
+ flash_options = {}
120
+ flash_options[:notice] = options[:notice] if options[:notice]
121
+ flash_options[:alert] = options[:alert] if options[:alert]
122
+
123
+ redirect_to redirect_path, flash_options
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IsItSpamRails
4
+ # Rails integration through Railtie
5
+ #
6
+ # Automatically configures the gem when Rails loads and adds controller helpers
7
+ class Railtie < Rails::Railtie
8
+ # Add spam checking functionality to ActionController::Base
9
+ initializer "is_it_spam_rails.add_controller_helpers" do
10
+ ActiveSupport.on_load(:action_controller) do
11
+ include IsItSpamRails::ControllerExtension
12
+ end
13
+ end
14
+
15
+ # Add rake tasks
16
+ rake_tasks do
17
+ load File.expand_path("../tasks/is_it_spam_rails.rake", __dir__)
18
+ end
19
+
20
+ # Add generators
21
+ generators do
22
+ require_relative "../generators/is_it_spam_rails/install_generator"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IsItSpamRails
4
+ VERSION = "2.0.0"
5
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "is_it_spam_rails/version"
4
+ require_relative "is_it_spam_rails/client"
5
+ require_relative "is_it_spam_rails/configuration"
6
+ require_relative "is_it_spam_rails/controller_extension"
7
+ require_relative "is_it_spam_rails/railtie" if defined?(Rails::Railtie)
8
+
9
+ # Rails integration gem for is-it-spam.com anti-spam service
10
+ #
11
+ # Provides Rails-specific functionality including:
12
+ # - Configuration through Rails initializers
13
+ # - Before action hooks for controllers
14
+ # - Rails generator for setup
15
+ module IsItSpamRails
16
+ class Error < StandardError; end
17
+
18
+ # Configuration error raised when credentials are missing or invalid
19
+ class ConfigurationError < Error; end
20
+
21
+ # API error raised when the API returns an error response
22
+ class ApiError < Error
23
+ # @return [Integer, nil] HTTP status code
24
+ attr_reader :status_code
25
+ # @return [String, nil] Raw response body
26
+ attr_reader :response_body
27
+
28
+ # Initialize API error
29
+ #
30
+ # @param message [String] Error message
31
+ # @param status_code [Integer, nil] HTTP status code
32
+ # @param response_body [String, nil] Raw response body
33
+ def initialize(message, status_code: nil, response_body: nil)
34
+ super(message)
35
+ @status_code = status_code
36
+ @response_body = response_body
37
+ end
38
+ end
39
+
40
+ # Rate limit error raised when API rate limits are exceeded
41
+ class RateLimitError < ApiError; end
42
+
43
+ # Validation error raised when request parameters are invalid
44
+ class ValidationError < ApiError
45
+ # @return [Hash] Field-specific validation errors
46
+ attr_reader :errors
47
+
48
+ # Initialize validation error
49
+ #
50
+ # @param message [String] Error message
51
+ # @param errors [Hash] Field-specific validation errors
52
+ # @param options [Hash] Additional options
53
+ def initialize(message, errors: {}, **options)
54
+ super(message, **options)
55
+ @errors = errors
56
+ end
57
+ end
58
+
59
+ # Global configuration for the gem
60
+ #
61
+ # @return [Configuration] The global configuration instance
62
+ def self.configuration
63
+ @configuration ||= Configuration.new
64
+ end
65
+
66
+ # Configure the gem with a block
67
+ #
68
+ # @yield [configuration] Configuration block
69
+ # @yieldparam configuration [Configuration] The configuration instance
70
+ def self.configure
71
+ yield(configuration)
72
+ end
73
+
74
+ # Get the configured client instance
75
+ #
76
+ # @return [Client] The configured client
77
+ def self.client
78
+ configuration.client
79
+ end
80
+
81
+ # Shortcut method for checking spam
82
+ #
83
+ # @param name [String] Name from the contact form
84
+ # @param email [String] Email address from the contact form
85
+ # @param message [String] Message content from the contact form
86
+ # @param custom_fields [Hash] Additional custom fields to check
87
+ # @param end_user_ip [String, nil] IP address of the end user filling the form (optional)
88
+ # @return [SpamCheckResult] The result of the spam check
89
+ def self.check_spam(name:, email:, message:, custom_fields: {}, end_user_ip: nil)
90
+ client.check_spam(name: name, email: email, message: message, custom_fields: custom_fields, end_user_ip: end_user_ip)
91
+ end
92
+
93
+ # Check API health
94
+ #
95
+ # @return [Boolean] true if the service is healthy
96
+ def self.health_check
97
+ client.health_check
98
+ end
99
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :is_it_spam do
4
+ desc "Install IsItSpamRails by creating an initializer"
5
+ task :install => :environment do
6
+ Rails::Generators.invoke("is_it_spam_rails:install")
7
+ end
8
+
9
+ desc "Test connection to Is It Spam API"
10
+ task :test_connection => :environment do
11
+ begin
12
+ puts "Testing connection to Is It Spam API..."
13
+
14
+ if IsItSpamRails.configuration.valid?
15
+ result = IsItSpamRails.health_check
16
+ if result
17
+ puts "✅ Connection successful!"
18
+ puts "API Key: #{IsItSpamRails.configuration.api_key[0..7]}..."
19
+ puts "Base URL: #{IsItSpamRails.configuration.base_url}"
20
+ else
21
+ puts "❌ API is not healthy (returned false)"
22
+ end
23
+ else
24
+ puts "❌ Configuration is invalid. Please check your API credentials."
25
+ puts "Current configuration:"
26
+ puts " API Key: #{IsItSpamRails.configuration.api_key.present? ? '[SET]' : '[NOT SET]'}"
27
+ puts " API Secret: #{IsItSpamRails.configuration.api_secret.present? ? '[SET]' : '[NOT SET]'}"
28
+ puts " Base URL: #{IsItSpamRails.configuration.base_url}"
29
+ end
30
+ rescue IsItSpamRails::ConfigurationError => e
31
+ puts "❌ Configuration error: #{e.message}"
32
+ rescue IsItSpamRails::ApiError => e
33
+ puts "❌ API error: #{e.message}"
34
+ puts "Status code: #{e.status_code}" if e.status_code
35
+ rescue StandardError => e
36
+ puts "❌ Unexpected error: #{e.message}"
37
+ end
38
+ end
39
+
40
+ desc "Test spam detection with sample data"
41
+ task :test_spam_check => :environment do
42
+ begin
43
+ puts "Testing spam detection with sample data..."
44
+
45
+ # Test with legitimate content
46
+ puts "\n--- Testing legitimate content ---"
47
+ legitimate_result = IsItSpamRails.check_spam(
48
+ name: "John Doe",
49
+ email: "john@example.com",
50
+ message: "I'm interested in your services. Could you please provide more information?"
51
+ )
52
+ puts "Result: #{legitimate_result.spam? ? 'SPAM' : 'LEGITIMATE'}"
53
+ puts "Confidence: #{(legitimate_result.confidence_score * 100).round(1)}%"
54
+ puts "Reasons: #{legitimate_result.spam_reasons.join(', ')}" if legitimate_result.spam_reasons.any?
55
+
56
+ # Test with spam content
57
+ puts "\n--- Testing spam content ---"
58
+ spam_result = IsItSpamRails.check_spam(
59
+ name: "Spammer",
60
+ email: "spam@suspicious.com",
61
+ message: "URGENT!!! FREE MONEY!!! Click here now to get rich quick! Act fast!"
62
+ )
63
+ puts "Result: #{spam_result.spam? ? 'SPAM' : 'LEGITIMATE'}"
64
+ puts "Confidence: #{(spam_result.confidence_score * 100).round(1)}%"
65
+ puts "Reasons: #{spam_result.spam_reasons.join(', ')}" if spam_result.spam_reasons.any?
66
+
67
+ puts "\n✅ Spam detection test completed!"
68
+
69
+ rescue IsItSpamRails::ConfigurationError => e
70
+ puts "❌ Configuration error: #{e.message}"
71
+ rescue IsItSpamRails::ValidationError => e
72
+ puts "❌ Validation error: #{e.message}"
73
+ puts "Errors: #{e.errors}" if e.errors.any?
74
+ rescue IsItSpamRails::ApiError => e
75
+ puts "❌ API error: #{e.message}"
76
+ puts "Status code: #{e.status_code}" if e.status_code
77
+ rescue StandardError => e
78
+ puts "❌ Unexpected error: #{e.message}"
79
+ end
80
+ end
81
+
82
+ desc "Show current configuration"
83
+ task :config => :environment do
84
+ puts "IsItSpamRails Configuration:"
85
+ puts " API Key: #{IsItSpamRails.configuration.api_key.present? ? IsItSpamRails.configuration.api_key[0..7] + '...' : '[NOT SET]'}"
86
+ puts " API Secret: #{IsItSpamRails.configuration.api_secret.present? ? '[SET]' : '[NOT SET]'}"
87
+ puts " Base URL: #{IsItSpamRails.configuration.base_url}"
88
+ puts " Timeout: #{IsItSpamRails.configuration.timeout} seconds"
89
+ puts " Valid: #{IsItSpamRails.configuration.valid? ? '✅' : '❌'}"
90
+ end
91
+ end
@@ -0,0 +1,4 @@
1
+ module IsItSpamRails
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: is_it_spam_rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Benjamin Deutscher
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-12-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '6.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '6.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: httparty
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.21'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.21'
41
+ - !ruby/object:Gem::Dependency
42
+ name: webmock
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.18'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.18'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sqlite3
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.4'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.4'
69
+ description: Provides Rails integration for the is-it-spam.com API with before_action
70
+ hooks for automatic spam detection in controllers.
71
+ email:
72
+ - ben@bdeutscher.org
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - CHANGELOG.md
78
+ - CLAUDE.md
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - lib/generators/is_it_spam_rails/install_generator.rb
83
+ - lib/generators/is_it_spam_rails/templates/initializer.rb
84
+ - lib/is_it_spam_rails.rb
85
+ - lib/is_it_spam_rails/client.rb
86
+ - lib/is_it_spam_rails/configuration.rb
87
+ - lib/is_it_spam_rails/controller_extension.rb
88
+ - lib/is_it_spam_rails/railtie.rb
89
+ - lib/is_it_spam_rails/version.rb
90
+ - lib/tasks/is_it_spam_rails.rake
91
+ - sig/is_it_spam_rails.rbs
92
+ homepage: https://is-it-spam.com
93
+ licenses:
94
+ - MIT
95
+ metadata:
96
+ allowed_push_host: https://rubygems.org
97
+ homepage_uri: https://is-it-spam.com
98
+ source_code_uri: https://github.com/its-benjamin-deutscher/is-it-spam.com
99
+ changelog_uri: https://github.com/its-benjamin-deutscher/is-it-spam.com/blob/main/lib/is_it_spam_rails/CHANGELOG.md
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: 3.1.0
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubygems_version: 3.5.23
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: Rails integration for is-it-spam.com anti-spam service
119
+ test_files: []