Giter Site home page Giter Site logo

sanitize_email's Introduction

sanitize_email

CI Build Test Coverage Maintainability Depfu


Liberapay Patrons Sponsor Me on Github

Buy me coffee donation button Patreon donate button

This gem allows you to override your mail delivery settings, globally or in a local context. It is like a Ruby encrusted condom for your email server, just in case it decides to have intercourse with other servers via sundry mail protocols.

Seriously though, this gem solves similar problems as the excellent mailcatcher gem, and mailcatcher solves those problems far more easily.

In addition, this gem solves problems that mailcatcher does not solve. I recommend using both!

To make an analogy, mailcatcher is akin to webmock, entirely preventing interaction with your real live mail server, while this gem allows you to effectively use your real live (production!) mail server, while intercepting and modifying recipeients on the way out, so that testing emails go to safe locations.

It is a bit like using the "test" Visa credit card number 4701322211111234 with a real payment gateway.

Encryption

Making special note of this use case because it is important for companies working on HIPAA-compliant products. When you are sending emails through an encrypted email provider, e.g. Paubox, testing your email in the aforementioned mailcatcher may not be enough.

If you want to test all the way through Paubox's system, but have the email go to a safe testing account address, then this is the gem for you.

Compatibility

⚙️ Compatible with all versions of Ruby >= 2.3, plus JRuby and Truffleruby. ⚙️ Compatible with all Ruby web Frameworks (Hanami, Roda, Sinatra, Rails). ⚙️ Compatible with all versions of Rails from 3.0 - 7.1+. ⚙️ Compatible with scripted usage of Mail gem outside a web framework. ⚙️ Compatible with sendgrid-actionmailer's support for personalizations, and will override email addresses there according to the configuration. ⚙️ If this gem is not compatible with your use case, and you'd like it to be, I'd like to hear about it!

It was a slog getting (very nearly) the entire compatibility matrix working with Github Actions, appraisal, and combustion, and I'm very interested in hearing about ways to improve it!

🛞 DVCS

This project does not trust any one version control system, so it abides the principles of "Distributed Version Control Systems"

Find this project on:

Any Of These DVCS
🐙hub 🧊berg 🛖hut 🧪lab
Project bundle add sanitize_email
1️⃣ name, license, docs, standards RubyGems.org License: MIT RubyDoc.info YARD Documentation SemVer 2.0.0 Keep-A-Changelog 1.0.0
2️⃣ version & activity Gem Version Total Downloads Download Rank Source Code Open PRs Closed PRs
3️⃣ maintenance & linting Maintainability Helpers Depfu Contributors Style
4️⃣ testing Supported Heads
5️⃣ coverage & security CodeClimate CodeCov Coveralls Security Policy CodeQL Code Coverage
6️⃣ resources Get help on Codementor Chat Blog Wiki
7️⃣ ... 💖 Liberapay Patrons Sponsor Me Follow Me on LinkedIn Find Me on WellFound: Find Me on CrunchBase My LinkTree Follow Me on Ruby.Social Follow Me on FLOSS.Social Follow Me on Mastodon.Social Tweet @ Peter 💻 🌏

Summary

It's particularly helpful when you want to prevent the delivery of email (e.g. in development/test environments) or alter the to/cc/bcc (e.g. in staging or demo environments) of all email generated from your application.

  • compatible without Rails! Can work with just the mail gem.
  • compatible with Rails >= 3.0. See gem versions 1.x for older versions of Rails.
  • compatible with Ruby >= 2.3. See gem versions 1.x for older versions of Ruby.
  • compatible with any Ruby app with a mail handler that uses the register_interceptor API (a la ActionMailer and mail gems)
  • configure it and forget it
  • little configuration required
  • solves common problems in ruby web applications that use email
  • provides test helpers and spec matchers to assist with testing email content delivery

Working Locally with Production Data

  1. Have a production site with live data
  2. Dump the live data and securely transfer it to another machine (e.g. rync -e ssh)
  3. Import it into a development database
  4. Test features which send out email (registration/signup, order placement, etc.)
  5. Emails get sent (in real-life!) but to sanitized email recipients
  6. Verify what they look like when sent
  7. Iterate on email content design
  8. No risk of emailing production addresses

Re-routing Email on a Staging or QA Server

Another very important use case for me is to transparently re-route email generated from a staging or QA server to an appropriate person. For example, it's common for us to set up a staging server for a client to use to view our progress and test out new features. It's important for any email that is generated from our web application be delivered to the client's inbox so that they can review the content and ensure that it's acceptable. Similarly, we set up QA instances for our own QA team and we use rails-caddy to allow each QA person to configure it specifically for them.

Testing Email from a Hot Production Server

If you install this gem on a production server (which I don't always do), you can load up script/console and override the to/cc/bcc on all emails for the duration of your console session. This allows you to poke and prod a live production instance, and route all email to your own inbox for inspection. The best part is that this can all be accomplished without changing a single line of your application code.

Monitoring all email sent by server to a backup account

You may want to add a BCC automatically (e.g. to [email protected]) to every email sent by your system, for customer service purposes, and this gem allows that. Note that this may not be a good idea for all systems, for many reasons, e.g security!

Using with a test suite as an alternative to the heavy email_spec

email_spec is a great gem, with awesome rspec matchers and helpers, but it has an undeclared dependency on ActionMailer. Sad face.

SanitizeEmail comes with some lightweight RspecMatchers covering most of what email_spec can do. It will help you test email functionality. It is useful when you are creating a gem to handle email features, or are writing a simple Ruby script, and don't want to pull in le Rails. SanitizeEmail has two dependencies, mail gem, and version_gem. Your Mail system just needs to conform to mail gem's register_interceptor API.

Install Like a Boss

In Gemfile:

gem 'sanitize_email'

Then:

$ bundle install

Setup with Ruby

keep scrolling for Rails, but read this for a better understanding of Magic

There are three ways SanitizeEmail can be turned on; in order of precedence they are:

  1. Only useful for local context. Inside a method where you will be sending an email, set SanitizeEmail.force_sanitize = true just prior to delivering it. Also useful in the console.

    SanitizeEmail.force_sanitize = true # by default it is nil
  2. If SanitizeEmail seems to not be sanitizing you have probably not registered the interceptor. SanitizeEmail tries to do this for you. Note: If you are working in an environment that has a Mail or Mailer class that uses the register_interceptor API, the interceptor will already have been registered by SanitizeEmail:

    # The gem will probably have already done this for you, but some really old versions of Rails may need you to do this manually:
    Mail.register_interceptor(SanitizeEmail::Bleach)

    Once registered, SanitizeEmail needs to be engaged:

    # in config/initializers/sanitize_email.rb
    SanitizeEmail::Config.configure {|config| config[:engage] = true }
  3. If you don't need to compute anything, then don't use this option, go with the previous option.

    SanitizeEmail::Config.configure {|config| config[:activation_proc] = Proc.new { true } } # by default :activation_proc is false

Examples

Only allow email to a specific domain

This works by ensuring that all recipients have the "allowed" domain. In other words, none of the recipients have a domain other than the allowed domain.

ALLOWED_DOMAIN = 'example.com'
# NOTE: you may need to check CC and BCC also, depending on your use case...
config[:activation_proc] = ->(message) do
   !Array(message.to).any? { |recipient| Mail::Address.new(recipient).domain != ALLOWED_DOMAIN }
end

Notes

Number 1, above, is the method used by the SanitizeEmail.sanitary block. If installed but not configured, sanitize_email DOES NOTHING. Until configured the defaults leave it turned off.

Troubleshooting

IMPORTANT: You may need to setup your own register_interceptor. If sanitize_email doesn't seem to be working for you find your Mailer/Mail class and try this:

# in config/initializers/sanitize_email.rb
Mail.register_interceptor(SanitizeEmail::Bleach)
SanitizeEmail::Config.configure {|config| config[:engage] = true }

If that causes an error you will know why sanitize_email doesn't work. Otherwise it will start working according to the rest of the configuration.

Setup With Rails

Create an initializer, if you are using rails, or otherwise configure:

SanitizeEmail::Config.configure do |config|
  config[:sanitized_to] =         'to@sanitize_email.org'
  config[:sanitized_cc] =         'cc@sanitize_email.org'
  config[:sanitized_bcc] =        'bcc@sanitize_email.org'
  # run/call whatever logic should turn sanitize_email on and off in this Proc:
  config[:activation_proc] =      Proc.new { %w(development test).include?(Rails.env) }
  config[:use_actual_email_prepended_to_subject] = true         # or false
  config[:use_actual_environment_prepended_to_subject] = true   # or false
  config[:use_actual_email_as_sanitized_user_name] = true       # or false
end

Keep in mind, this is ruby (and possibly rails), so you can add conditionals or utilize different environment.rb files to customize these settings on a per-environment basis.

Override the override

But wait there's more:

Let's say you have a method in your model that you can call to test the signup email. You want to be able to test sending it to any user at any time... but you don't want the user to ACTUALLY get the email, even in production. A dilemma, yes? Not anymore!

To override the environment based switch use force_sanitize, which is normally nil, and ignored by default. When set to true or false it will turn sanitization on or off:

  SanitizeEmail.force_sanitize = true

When testing your email in a console, you can manipulate how email will be handled in this way.

There are also two methods that take a block and turn SanitizeEmail on or off (see section on Thread Safety below):

Regardless of the Config settings of SanitizeEmail you can do a local override to force unsanitary email in any environment.

  SanitizeEmail.unsanitary do
    Mail.deliver do
      from      '[email protected]'
      to        '[email protected]' # Will actually be sent to the specified address, not sanitized
      reply_to  '[email protected]'
      subject   'subject'
    end
  end

Regardless of the Config settings of SanitizeEmail you can do a local override to send sanitary email in any environment. You have access to all the same configuration options in the parameter hash as you can set in the actual SanitizeEmail.configure block.

  SanitizeEmail.sanitary({:sanitized_to => '[email protected]'}) do # these config options are merged with the globals
    Mail.deliver do
      from      '[email protected]'
      to        '[email protected]' # Will actually be sent to the override addresses, in this case: [email protected]
      reply_to  '[email protected]'
      subject   'subject'
    end
  end

Configuration Options

As used in the "Description" column below, engaged means: SanitizeEmail.activate?(message) # => true. This happens in a few different ways, and two of them are in the config below (engage and activation_proc).

Option Type (Yard format) Description
sanitized_to [String, Array[String]] (when engaged) Override CC field with these addresses
sanitized_cc [String, Array[String]] (when engaged) Override CC field with these addresses
sanitized_bcc [String, Array[String]] (when engaged) Override BCC field with these addresses
good_list [Array[String]] (when engaged) Email addresses to allow to pass-through without overriding
bad_list [Array[String]] (when engaged) Email addresses to be removed from message's TO, CC, & BCC
environment [String, #to_s, Proc, Lambda, #call] (when engaged) The environment value to use wherever it is added to message (e.g. in the subject line)
use_actual_email_as_sanitized_user_name [Boolean] (when engaged) Use "real" email address as username for sanitized email address (e.g. "real at example.com [email protected]")
use_actual_email_prepended_to_subject [Boolean] (when engaged) Use "real" email address prepended to subject (e.g. "real at example.com Original Subject")
use_actual_environment_prepended_to_subject [Boolean] (when engaged) Use environment prepended to subject (e.g. "{{ STAGING }} Original Subject")
engage [Boolean, nil] Boolean will turn engage or disengage this gem, while nil ignores this setting and instead checks activation_proc
activation_proc [Proc, Lambda, #call] When checked, due to engage: nil, the result will either engage or disengage this gem

Thread Safety

So long as you don't change the config after initializing it at runtime, you'll be fine. Like many Ruby tools' config objects, it is a single config object, shared by all threads. The helpers like sanitary, unsanitary, janitor, and force_sanitize are intended to be used in single threaded environments, like a test suite, or a console session.

I doubt I'll ever have a need for runtime reconfiguration of the config, so I doubt I'll ever have a reason to make it "more" thread safe than it is now, but PRs are welcome!

Use sanitize_email in your test suite!

rspec

In your spec_helper.rb:

require 'sanitize_email'
# rspec matchers are *not* loaded by default in sanitize_email, as it is not primarily a gem for test suites.
require 'sanitize_email/rspec_matchers'

SanitizeEmail::Config.configure do |config|
  config[:sanitized_to] =         '[email protected]'
  config[:sanitized_cc] =         '[email protected]'
  config[:sanitized_bcc] =        '[email protected]'
  # run/call whatever logic should turn sanitize_email on and off in this Proc.
  # config[:activation_proc] =      Proc.new { true }
  # Since this configuration is *inside* the spec_helper, it might be assumed that we always want to sanitize.  If we don't want to it can be easily manipulated with SanitizeEmail.unsanitary and SanitizeEmail.sanitary block helpers.
  # Thus instead of using the Proc (slower) we just engage it always:
  config[:engage] = true
  config[:use_actual_email_prepended_to_subject] = true         # or false
  config[:use_actual_environment_prepended_to_subject] = true   # or false
  config[:use_actual_email_as_sanitized_user_name] = true       # or false
end

# If your mail system is not one that sanitize_email automatically configures an interceptor for (ActionMailer, Mail) 
# then you will need to do the equivalent for whatever Mail system you are using.

RSpec.configure do |config|
  # ...
  # From sanitize_email gem
  config.include SanitizeEmail::RspecMatchers
end

context "an email test" do
  subject { Mail.deliver(@message_hash) }
  it { should have_to "[email protected]" }
end

have_* matchers

These will look for an email address in any of the following mail attributes:

[:from, :to, :cc, :bcc, :subject, :reply_to]

Example:

context "the subject line must have the email address [email protected]" do
  subject { Mail.deliver(@message_hash) }
  it { should have_subject "[email protected]" }
end

be_* matchers

These will look for a matching string in any of the following

:from, :to, :cc, :bcc, :subject, :reply_to

Example:

context "the subject line must have the string 'foobarbaz'" do
  subject { Mail.deliver(@message_hash) }
  it { should be_subject "foobarbaz" }
end

have_to_username matcher

The username in the :to field is when the :to field is formatted like this:

"Peter Boling" <[email protected]>

Example:

context "the to field must have the username 'Peter Boling'" do
  subject { Mail.deliver(@message_hash) }
  it { should have_to_username "Peter Boling" }
end

have_sanitized_to_header matcher

Matches any part of the value of the first sanitized to header ("X-Sanitize-Email-To"), which could be formatted like this:

"Peter Boling" <[email protected]>

NOTE: It won't match subsequent headers like "X-Sanitize-Email-To-2", or "X-Sanitize-Email-To-3".

Example:

context "the first 'X-Sanitize-Email-To' header must have the username 'Peter Boling'" do
  subject { Mail.deliver(@message_hash) }
  it { should have_sanitized_to_header "Peter Boling" }
end

have_cc_username matcher

The username in the :cc field is when the :c field is formatted like this:

"Peter Boling" <[email protected]>

Example:

context "the cc field must have the username 'Peter Boling'" do
  subject { Mail.deliver(@message_hash) }
  it { should have_cc_username "Peter Boling" }
end

have_sanitized_cc_header matcher

Matches any part of the value of the first sanitized cc header ("X-Sanitize-Email-Cc"), which could be formatted like this:

"Peter Boling" <[email protected]>

NOTE: It won't match subsequent headers like "X-Sanitize-Email-Cc-2", or "X-Sanitize-Email-Cc-3".

Example:

context "the first 'X-Sanitize-Email-Cc' header must have the username 'Peter Boling'" do
  subject { Mail.deliver(@message_hash) }
  it { should have_sanitized_cc_header "Peter Boling" }
end

non-rspec (Test::Unit, mini-test, etc)

In your setup file:

require 'sanitize_email'
# test helpers are *not* loaded by default in sanitize_email, as it is not primarily a gem for test suites.
require 'sanitize_email/test_helpers'

SanitizeEmail::Config.configure do |config|
  config[:sanitized_to] =         '[email protected]'
  config[:sanitized_cc] =         '[email protected]'
  config[:sanitized_bcc] =        '[email protected]'
  # run/call whatever logic should turn sanitize_email on and off in this Proc.
  # config[:activation_proc] =      Proc.new { true }
  # Since this configuration is *inside* the spec_helper, it might be assumed that we always want to sanitize.  If we don't want to it can be easily manipulated with SanitizeEmail.unsanitary and SanitizeEmail.sanitary block helpers.
  # Thus instead of using the Proc (slower) we just engage it always:
  config[:engage] = true
  config[:use_actual_email_prepended_to_subject] = true         # or false
  config[:use_actual_environment_prepended_to_subject] = true   # or false
  config[:use_actual_email_as_sanitized_user_name] = true       # or false
end

# If your mail system is not one that sanitize_email automatically configures an interceptor for (ActionMailer, Mail) 
# then you will need to do the equivalent for whatever Mail system you are using.

# You need to know what to do here... somehow get the methods into rhw scope of your tests.
# Something like this maybe?
include SanitizeEmail::TestHelpers
# Look here to see what it gives you:
# https://github.com/pboling/sanitize_email/blob/master/lib/sanitize_email/test_helpers.rb

Deprecations

Sometimes things get deprecated (meaning they still work, but are noisy about it). If this happens to you, and you like your head in the sand, call this number:

SanitizeEmail::Deprecation.deprecate_in_silence = true

Authors

Peter Boling is the original author of the code, and current maintainer.

Thanks to John Trupiano for turning Peter's original Rails plugin into this gem!

🤝 Contributing

See CONTRIBUTING.md

You can help!

Take a look at the reek list which is the file called REEK and start fixing things.

To refresh the reek list:

bundle exec reek > REEK

Then follow these instructions:

  1. Fork the repository
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Make some fixes.
  4. Commit your changes (git commit -am 'Added some feature')
  5. Push to the branch (git push origin my-new-feature)
  6. Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
  7. Create new Pull Request.

🌈 Contributors

Contributors

Contributor tiles (GitHub only) made with contributors-img.

Learn more about, or become one of, our 🎖 contributors on:

Any Of These DVCS
🐙hub contributors 🧊berg contributors 🛖hut contributors 🧪lab contributors

Star History

Star History Chart

Running Specs

The basic compatibility matrix:

appraisal install
appraisal rake test

Sometimes also:

BUNDLE_GEMFILE=gemfiles/vanilla.gemfile appraisal update

NOTE: This results in bad paths to the gemspec. gemspec path: "../../" needs to be replaced with gemspec path: "../" in each Appraisal gemfile.

Except, is unlikely to be possible to install all of the supported Rubies & Railsies in a single container... See the various github action workflows for more inspiration on running certain oldies.

Code Coverage

Coverage Graph

🪇 Code of Conduct

Everyone interacting in this project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.

📌 Versioning

This Library adheres to Semantic Versioning 2.0.0. Violations of this scheme should be reported as bugs. Specifically, if a minor or patch version is released that breaks backward compatibility, a new version should be immediately released that restores compatibility. Breaking changes to the public API will only be introduced with new major versions.

To get a better understanding of how SemVer is intended to work over a project's lifetime, read this article from the creator of SemVer:

As a result of this policy, you can (and should) specify a dependency on these libraries using the Pessimistic Version Constraint with two digits of precision.

For example:

spec.add_dependency "sanitize_email", "~> 2.0"

References

📄 License

The gem is available as open source under the terms of the MIT License License: MIT. See LICENSE.txt for the official Copyright Notice.

© Copyright

sanitize_email's People

Contributors

altonymous avatar bernespinoza avatar bitdeli-chef avatar depfu[bot] avatar dgm avatar direction avatar iainbeeston avatar joeyparis avatar jtrupiano avatar koppen avatar lgtm-com[bot] avatar milgner avatar mslade-fairfax avatar nfedyashev avatar olleolleolle avatar pboling avatar rochers avatar rrrene avatar tenpaiyomi avatar theo-bittencourt avatar unpublishedworks avatar zacheryph avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sanitize_email's Issues

Email is not sanitized in rspec

I have installed sanitize_email (1.1.4) an am having issues with my rspec test.
I tried following the example in your README, this is /spec/support/sanitize_email.rb:

require "sanitize_email"
require "sanitize_email/rspec_matchers"

RSpec.configure do |config|
  config.include SanitizeEmail::RspecMatchers
end

describe "SanitizeEmail" do
  context "receiver email should be sanitized" do
    subject { Mail.new(from: "[email protected]", to: "[email protected]", subject: "test") }
    it { should be_to "[email protected]" }
  end
end

And in my /config/environments/test.rb:

SanitizeEmail::Config.configure do |config|
  config[:activation_proc] = Proc.new { true }
  config[:sanitized_to] = "[email protected]"
end

The test fails with:

1) SanitizeEmail receiver email should be sanitized
     Failure/Error: it { should be_to "[email protected]" }
       expected #<Mail::Message:46409500, Multipart: false, Headers: <From: [email protected]>, <To: [email protected]>, <Subject: test>> to be to "[email protected]"
     # ./spec/support/sanitize_email.rb:11:in `block (3 levels) in <top (required)>'

Force sanitize doesn't seem to work

force_sanitize isn't working for me, and neither is the unsanitized block method. When I either set force_sanitize to false, or use unsanitized nothing gets sent (in fact when I run unsanitized the block given doesn't seem to run at all, judging by "puts" statements I put in for debugging). I haven't had a chance to look into why this is happening yet.

Undefined local variable or method config (v2.0.5)

Once we updated sanitize_email to 2.0.5, our staging assets:precompile step started failing, down below are the logs with the relevant part.

We are using Ruby 3.3.0 and Rails 7.1.3.2.

I assume this issue was caused by this new file on v2.0.5, but I didn't dig deeper to investigate it.

LMK if there's anything else I can provide to help you debug/investigate this issue. 🙏

Logs

-----> Preparing app for Rails asset pipeline
Running: rake assets:precompile
rake aborted!

NameError: undefined local variable or method `config' for an instance of #<Class:0x00007f2680aad400> (NameError)

     config.action_mailer.register_interceptor(SanitizeEmail::Bleach)
     ^^^^^^

/vendor/bundle/ruby/3.3.0/gems/sanitize_email-2.0.5/lib/sanitize_email/engine_v6.rb:12:in `block in <class:EngineV5>'
/vendor/bundle/ruby/3.3.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:448:in `instance_exec'
/vendor/bundle/ruby/3.3.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:448:in `block in make_lambda'
/vendor/bundle/ruby/3.3.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:202:in `block (2 levels) in halting'
/vendor/bundle/ruby/3.3.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:707:in `block (2 levels) in default_terminator'
/vendor/bundle/ruby/3.3.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:706:in `catch'
/vendor/bundle/ruby/3.3.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:706:in `block in default_terminator'
/vendor/bundle/ruby/3.3.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:203:in `block in halting'
/vendor/bundle/ruby/3.3.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:598:in `block in invoke_before'
/vendor/bundle/ruby/3.3.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:598:in `each'
/vendor/bundle/ruby/3.3.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:598:in `invoke_before'
/vendor/bundle/ruby/3.3.0/gems/activesupport-7.1.3.2/lib/active_support/callbacks.rb:109:in `run_callbacks'
/vendor/bundle/ruby/3.3.0/gems/activesupport-7.1.3.2/lib/active_support/reloader.rb:96:in `prepare!'
/vendor/bundle/ruby/3.3.0/gems/railties-7.1.3.2/lib/rails/application/finisher.rb:74:in `block in <module:Finisher>'
/vendor/bundle/ruby/3.3.0/gems/railties-7.1.3.2/lib/rails/initializable.rb:32:in `instance_exec'
/vendor/bundle/ruby/3.3.0/gems/railties-7.1.3.2/lib/rails/initializable.rb:32:in `run'
/vendor/bundle/ruby/3.3.0/gems/railties-7.1.3.2/lib/rails/initializable.rb:61:in `block in run_initializers'
/vendor/bundle/ruby/3.3.0/gems/railties-7.1.3.2/lib/rails/initializable.rb:60:in `run_initializers'
/vendor/bundle/ruby/3.3.0/gems/railties-7.1.3.2/lib/rails/application.rb:426:in `initialize!'
/config/environment.rb:7:in `<main>'
/vendor/bundle/ruby/3.3.0/gems/bootsnap-1.18.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
/vendor/bundle/ruby/3.3.0/gems/zeitwerk-2.6.13/lib/zeitwerk/kernel.rb:34:in `require'
/vendor/bundle/ruby/3.3.0/gems/railties-7.1.3.2/lib/rails/application.rb:402:in `require_environment!'
/vendor/bundle/ruby/3.3.0/gems/railties-7.1.3.2/lib/rails/application.rb:588:in `block in run_tasks_blocks'
/Rakefile:19:in `invoke'
/vendor/bundle/ruby/3.3.0/gems/sprockets-rails-3.4.2/lib/sprockets/rails/task.rb:61:in `block (2 levels) in define'
/Rakefile:14:in `invoke'
Tasks: TOP => environment
(See full trace by running task with --trace)

What's the correct way to deduplicate sanitized addresses?

Hi, thank you for creating this gem.

I'm curious about what the best way to deduplicate sanitized addresses is. Right now we're sanitizing in our staging environment to prevent external sends, but replaces of multiple addresses end up with the same address multiple times in the final result. This causes DuplicatePersonalization errors from Sendgrid, which prohibits this. Right now I'm doing this with the following monkey patch:

module SanitizeEmail
  class Bleach
    class << self
      alias original_delivering_mail delivering_email
    end

    def self.delivering_email(message)
      original_delivering_mail(message)

      message.to = message.to&.uniq
      message.cc = message.cc&.uniq
      message.bcc = message.bcc&.uniq
    end
  end
end

Is there a more correct way to do this?

Rails.env check fails on old version of Rails

I have an application that I believe is running Rails 2.2.2. In custom_environments.rb, there is code to use RAILS_ENV when Rails.env is not available:

local_environments.include?(defined?(Rails) ? Rails.env : RAILS_ENV)

On my version of Rails, it appears that the Rails module exists, but the env method on it does not. I believe you should be doing Rails.method_defined? :env as well before you make the call to Rails.env.

Thanks!

config[:sanitized_bcc] key does not work

When configuring the gem, setting the key config[:sanitized_bcc] should make all sanitized emails go to config[:sanitized_to] AND to this config[:sanitized_bcc] email. However, it is not happening.

undefined method `delivering_email' for SanitizeEmail::Bleach:Class

In a Rails 3.1.7 app, Ruby 1.9.3p194, using sanitize_email (1.0.0.rc1) for the first time, I see the following running tests:

undefined method `delivering_email' for SanitizeEmail::Bleach:Class

My initializer is:

SanitizeEmail::Config.configure do |config|
  config[:sanitized_recipients] = '[email protected]'
  config[:local_environment_proc] =   Proc.new { %w(development test stage demo).include?(Rails.env) }
  config[:use_actual_email_prepended_to_subject] = true   # or false
  config[:use_actual_email_as_sanitized_user_name] = true # or false
end

Full backtrace is:

NoMethodError: undefined method `delivering_email' for SanitizeEmail::Bleach:Class
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/mail-2.3.3/lib/mail/mail.rb:230:in `block in inform_interceptors'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/mail-2.3.3/lib/mail/mail.rb:229:in `each'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/mail-2.3.3/lib/mail/mail.rb:229:in `inform_interceptors'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/mail-2.3.3/lib/mail/message.rb:218:in `inform_interceptors'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/mail-2.3.3/lib/mail/message.rb:228:in `deliver'
/Users/jay/src/plm/robomo/app/models/ticket.rb:167:in `send_notification'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.1.7/lib/active_support/callbacks.rb:449:in `_run_create_callbacks'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.1.7/lib/active_support/callbacks.rb:81:in `run_callbacks'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.1.7/lib/active_record/callbacks.rb:268:in `create'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.1.7/lib/active_record/persistence.rb:294:in `create_or_update'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.1.7/lib/active_record/callbacks.rb:264:in `block in create_or_update'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.1.7/lib/active_support/callbacks.rb:418:in `block in _run_save_callbacks'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.1.7/lib/active_support/callbacks.rb:207:in `block in _conditional_callback_around_374'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/state_machine-1.0.1/lib/state_machine/integrations/active_record.rb:383:in `block in around_save'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/state_machine-1.0.1/lib/state_machine/transition_collection.rb:149:in `block in run_actions'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/state_machine-1.0.1/lib/state_machine/transition_collection.rb:169:in `catch_exceptions'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/state_machine-1.0.1/lib/state_machine/transition_collection.rb:147:in `run_actions'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/state_machine-1.0.1/lib/state_machine/transition_collection.rb:132:in `run_callbacks'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/state_machine-1.0.1/lib/state_machine/transition_collection.rb:211:in `run_callbacks'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/state_machine-1.0.1/lib/state_machine/transition_collection.rb:63:in `block (2 levels) in perform'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/state_machine-1.0.1/lib/state_machine/transition_collection.rb:63:in `catch'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/state_machine-1.0.1/lib/state_machine/transition_collection.rb:63:in `block in perform'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/state_machine-1.0.1/lib/state_machine/transition_collection.rb:185:in `within_transaction'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/state_machine-1.0.1/lib/state_machine/transition_collection.rb:62:in `perform'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/state_machine-1.0.1/lib/state_machine/integrations/active_record.rb:383:in `around_save'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.1.7/lib/active_support/callbacks.rb:302:in `_callback_around_301'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.1.7/lib/active_support/callbacks.rb:206:in `_conditional_callback_around_374'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.1.7/lib/active_support/callbacks.rb:390:in `_run_save_callbacks'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.1.7/lib/active_support/callbacks.rb:81:in `run_callbacks'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.1.7/lib/active_record/callbacks.rb:264:in `create_or_update'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.1.7/lib/active_record/persistence.rb:57:in `save!'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.1.7/lib/active_record/validations.rb:56:in `save!'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.1.7/lib/active_record/attribute_methods/dirty.rb:33:in `save!'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.1.7/lib/active_record/transactions.rb:246:in `block in save!'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.1.7/lib/active_record/transactions.rb:295:in `block in with_transaction_returning_status'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.1.7/lib/active_record/connection_adapters/abstract/database_statements.rb:194:in `transaction'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.1.7/lib/active_record/transactions.rb:208:in `transaction'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.1.7/lib/active_record/transactions.rb:293:in `with_transaction_returning_status'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.1.7/lib/active_record/transactions.rb:246:in `save!'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/factory_girl-1.3.3/lib/factory_girl/proxy/create.rb:6:in `result'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/factory_girl-1.3.3/lib/factory_girl/factory.rb:330:in `run'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/factory_girl-1.3.3/lib/factory_girl/factory.rb:273:in `create'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/factory_girl-1.3.3/lib/factory_girl/factory.rb:304:in `default_strategy'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/factory_girl-1.3.3/lib/factory_girl.rb:20:in `Factory'
/Users/jay/src/plm/robomo/test/functional/tickets_controller_test.rb:67:in `block (2 levels) in <class:TicketsControllerTest>'
/Users/jay/src/plm/robomo/test/functional/tickets_controller_test.rb:67:in `times'
/Users/jay/src/plm/robomo/test/functional/tickets_controller_test.rb:67:in `block in <class:TicketsControllerTest>'
/Users/jay/.rvm/gems/ruby-1.9.3-p194/gems/mocha-0.9.12/lib/mocha/integration/mini_test/version_201_to_202.rb:27:in `run'

HoundCI does not have the same rules as RuboCop

HoundCI and RuboCop have conflicting rules that make committing code requests difficult as what satisfies local testing doesn't satisfy pull request testing.

I personally prefer the style choices we have set in Rubocop, but at least one of the two configurations should be updated to match each other.

Not automatically intercepting in Rails any longer

After upgrading from sanitize_email 2.0.4 to 2.0.6, it looks like e-mails have stopped being intercepted in a Rails 7.1 app by default. If I manually call Mail.register_interceptor(SanitizeEmail::Bleach), this does seem to fix the interception in the app, but I think something is maybe amiss with the new Engine integration for Rails 6+ in 2.0.6:

v2.0.4...v2.0.6#diff-c6ad5f4fb841393b5d4fee8dac9a3be490baf334851b4cae3adcb90c1096a4e2
v2.0.4...v2.0.6#diff-6b14c6f0901cbec9229126c0ae73f28b72421a354fc0aead148fa406f5f665ca

I think the issue is maybe related to the new EngineV6 calling:

Rails.application.config.action_mailer.register_interceptor(SanitizeEmail::Bleach)

I believe the issue is that register_interceptor may not actually be doing anything in this case. At least in the Rails 7.1 app I'm testing, Rails.application.config.action_mailer is actually an instance of ActiveSupport::OrderedOptions, so this is more like a hash that responds to method calls as hash lookups too. But that means that I don't think register_interceptor() is actually calling any type of underlying method. Instead, I think it's just looking for a :register_interceptor attribute on the hash of mailer config data, and returning nil since it doesn't exist.

So I think this means that calling Rails.application.config.action_mailer.register_interceptor(SanitizeEmail::Bleach) doesn't actually seem to ever call the underlying ActionMailer::Base.register_interceptor or Mail.register_interceptor methods. You can see how any method passed to action_mailer will not raise an exception, but I don't believe it's actually calling anything (I think it's just acting more like a hash accessor). This differs from how a real call to ActionMailer::Base.register_interceptor will react:

[1] pry(main)> Rails.application.config.action_mailer.register_interceptor(SanitizeEmail::Bleach)
=> nil
[2] pry(main)> Rails.application.config.action_mailer.register_interceptor
=> nil
[3] pry(main)> Rails.application.config.action_mailer.register_interceptor_foo(SanitizeEmail::Bleach)
=> nil
[4] pry(main)> ActionMailer::Base.register_interceptor
ArgumentError: wrong number of arguments (given 0, expected 1)
from /dev-cache/usr/local/bundle/gems/actionmailer-7.1.3.4/lib/action_mailer/base.rb:548:in `register_interceptor'

So I think this might mean that this Engine for Rails 6+ isn't actually setting the interceptor by default in version 2.0.6. It sounds like maybe a deprecation warning is the reason for the new approach in Rails 6+, but for whatever it's worth I don't actually see any deprecation warnings when using sanitize_email 2.0.4 under Rails 7.1 (but I realize I might not be exercising the right thing, since there's probably a lot of different possible app setups).

Thanks!

Uninitialize constant SanitizeEmail

Hey,

I have smtp.rb in config/initializers. In this file I have standard config SanitizeEmail gem.
When I tried to use sanitize_email gem on production I got error: Uninitialize constant SanitizeEmail in the unicorn.log.

VPS:
ruby 1.9.3
rails 3.2.13
unicorn
nginx
sanitize_email 1.0.6

the gem doesn't list its dependencies

I'm not sure if it's really a problem with the sanitize_email gem itself, depndency handling issue in rubygems or just my wrong understanding.

sanitize_email gem reports only one dependency - actionmailer >=0. I have it satisfied (2.3.5). When I install sanitize_email gem it install bunch of other stuff - some of which I don't want in my gemset.

So does sanitize_email have more dependencies, or not?

Here's what I do on a fresh rvm gemset:

$ gem install -v 2.3.5 rails
Successfully installed activesupport-2.3.5
Successfully installed activerecord-2.3.5
Successfully installed rack-1.0.1
Successfully installed actionpack-2.3.5
Successfully installed actionmailer-2.3.5
Successfully installed activeresource-2.3.5
Successfully installed rails-2.3.5
7 gems installed

$ gem install sanitize_email
Successfully installed sanitize_email-0.3.7
Successfully installed activesupport-3.0.3
Successfully installed builder-2.1.2
Successfully installed i18n-0.5.0
Successfully installed activemodel-3.0.3
Successfully installed rack-1.2.1
Successfully installed rack-test-0.5.6
Successfully installed rack-mount-0.6.13
Successfully installed tzinfo-0.3.23
Successfully installed abstract-1.0.0
Successfully installed erubis-2.6.6
Successfully installed actionpack-3.0.3
Successfully installed mime-types-1.16
Successfully installed polyglot-0.3.1
Successfully installed treetop-1.4.9
Successfully installed mail-2.2.12

# gem dependency sanitize_email
Gem sanitize_email-0.3.7
  actionmailer (>= 0, runtime)

And my environment:

$ gem -v
1.3.7

$ rvm info

ruby-1.8.7-p302@bundler6:

  system:
    uname:       "Darwin Makak.local 10.5.0 Darwin Kernel Version 10.5.0: Fri Nov  5 23:20:39 PDT 2010; root:xnu-1504.9.17~1/RELEASE_I386 i386"
    bash:        "/bin/bash => GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin10.0)"
    zsh:         "/bin/zsh => zsh 4.3.9 (i386-apple-darwin10.0)"

  rvm:
    version:      "rvm 1.1.2 by Wayne E. Seguin ([email protected]) [http://rvm.beginrescueend.com/]"

  ruby:
    interpreter:  "ruby"
    version:      "1.8.7"
    date:         "2010-08-16"
    platform:     "i686-darwin10.4.0"
    patchlevel:   "2010-08-16 patchlevel 302"
    full_version: "ruby 1.8.7 (2010-08-16 patchlevel 302) [i686-darwin10.4.0]"

  homes:
    gem:          "/Users/wojciech/.rvm/gems/ruby-1.8.7-p302@bundler6"
    ruby:         "/Users/wojciech/.rvm/rubies/ruby-1.8.7-p302"

  binaries:
    ruby:         "/Users/wojciech/.rvm/rubies/ruby-1.8.7-p302/bin/ruby"
    irb:          "/Users/wojciech/.rvm/rubies/ruby-1.8.7-p302/bin/irb"
    gem:          "/Users/wojciech/.rvm/rubies/ruby-1.8.7-p302/bin/gem"
    rake:         "/Users/wojciech/.rvm/gems/ruby-1.8.7-p302@global/bin/rake"

  environment:
    PATH:         "/Users/wojciech/.rvm/gems/ruby-1.8.7-p302@bundler6/bin:/Users/wojciech/.rvm/gems/ruby-1.8.7-p302@global/bin:/Users/wojciech/.rvm/rubies/ruby-1.8.7-p302/bin:/Users/wojciech/.rvm/bin:/opt/local/bin:/opt/local/sbin:/sw/bin:/sw/sbin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin:/usr/X11R6/bin"
    GEM_HOME:     "/Users/wojciech/.rvm/gems/ruby-1.8.7-p302@bundler6"
    GEM_PATH:     "/Users/wojciech/.rvm/gems/ruby-1.8.7-p302@bundler6:/Users/wojciech/.rvm/gems/ruby-1.8.7-p302@global"
    MY_RUBY_HOME: "/Users/wojciech/.rvm/rubies/ruby-1.8.7-p302"
    IRBRC:        "/Users/wojciech/.rvm/rubies/ruby-1.8.7-p302/.irbrc"
    RUBYOPT:      ""
    gemset:       "bundler6"

Allow custom subject prefixes

It'd be nice to have my own variable prepended to the subject rather than being limited to environment and email recipients.

Bonus points, allow this as proc so the prefix is dynamically generated on a per mail basis

Emails are not sanitized when sent through SendGrid `personalizations` argument

When sending multiple emails via SendGrid's personalizations argument the sanitized recipients are not applied (although the other sanitizations seem to be. I haven't been able to dive deep into the difference of how this option sends to give more insight yet, but I wanted to get this issue recorded. I'll do some more research tomorrow and update this issue.

mail({
    to: "account@external_domain.com",  # Properly sanitized
    from: "[email protected]",
    subject: "Hello",
    body: "World!",
    personalizations: [
        {
            to: [{ email: "second_account@external_domain.com"}], # Not properly sanitized, sent to actual recipient
            bcc: [{ email: "copy@external_domain.com"}] # Also, not properly sanitized, sent to actual recipient
        },
    ]
})

Under normal circumstances, SendGrid would create two emails from this request. One to account@external_domain.com and one to second_account@external_domain.com with a bcc to copy@external_domain.com.


Update 2020-08-17 11:03AM ET
This is more likely to be the sendgrid-actionmailer gem than the sendgrid-ruby gem.

"DEPRECATION WARNING: Initialization autoloaded the constants ActionText::ContentHelper and ActionText::TagHelper" on Rails 6.1+

Hi, thank you for making this gem.

When used on Rails 6.1 and later, this gem causes the following warning at startup:

DEPRECATION WARNING: Initialization autoloaded the constants ActionText::ContentHelper and ActionText::TagHelper.

Being able to do this is deprecated. Autoloading during initialization is going
to be an error condition in future versions of Rails.

Reloading does not reboot the application, and therefore code executed during
initialization does not run again. So, if you reload ActionText::ContentHelper, for example,
the expected changes won't be reflected in that stale Module object.

These autoloaded constants have been unloaded.

In order to autoload safely at boot time, please wrap your code in a reloader
callback this way:

    Rails.application.reloader.to_prepare do
      # Autoload classes and modules needed at boot time here.
    end

That block runs when the application boots, and every time there is a reload.
For historical reasons, it may run twice, so it has to be idempotent.

Check the "Autoloading and Reloading Constants" guide to learn more about how
Rails autoloads and reloads.
 (called from <top (required)> at /home/mpd/git-sources/catchandrelease/config/environment.rb:7)

The actual issue is probably the reference to ActionController::Base in engine.rb.

The rails-side issue regarding this warning (with a lot more context) is here.

strange repeating sanitation

I have a weird output in my sanitized emails:

Subject: (mortonda at dgrmm.net) (mortonda at dgrmm.net) (mortonda at
 dgrmm.net) (mortonda at dgrmm.net) (mortonda at dgrmm.net) (mortonda at
 dgrmm.net) (mortonda at dgrmm.net) (mortonda at dgrmm.net) (mortonda at
 dgrmm.net) (mortonda at dgrmm.net) (mortonda at dgrmm.net) (mortonda at
 dgrmm.net) (mortonda at dgrmm.net) (mortonda at dgrmm.net) (mortonda at
 dgrmm.net) (mortonda at dgrmm.net) (joe1 at example.com) Confirmation
 instructions
Mime-Version: 1.0
Content-Type: text/html;
 charset=UTF-8
Content-Transfer-Encoding: 7bit
X-Virus-Scanned: Maia Mailguard 1.0.2

: 
: 
: 
: 
: 
: 
: 
: 
: 
: 
: 
: 
: 
: 
: 
: 
: 
X-Sanitize-Email-To: [email protected]
X-Sanitize-Email-To: [email protected]
X-Sanitize-Email-To: [email protected]
X-Sanitize-Email-To: [email protected]
X-Sanitize-Email-To: [email protected]
X-Sanitize-Email-To: [email protected]
X-Sanitize-Email-To: [email protected]
X-Sanitize-Email-To: [email protected]
X-Sanitize-Email-To: [email protected]
X-Sanitize-Email-To: [email protected]
X-Sanitize-Email-To: [email protected]
X-Sanitize-Email-To: [email protected]
X-Sanitize-Email-To: [email protected]
X-Sanitize-Email-To: [email protected]
X-Sanitize-Email-To: [email protected]
X-Sanitize-Email-To: [email protected]
X-Sanitize-Email-To: [email protected]

<p>Welcome [email protected]!</p>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.