Giter Site home page Giter Site logo

roadie's Introduction

Roadie

Code Climate Code coverage status Gem Passive maintenance

⚠️ This gem is now in passive maintenance mode. (more)

Making HTML emails comfortable for the Ruby rockstars

Roadie tries to make sending HTML emails a little less painful by inlining stylesheets and rewriting relative URLs for you inside your emails.

How does it work?

Email clients have bad support for stylesheets, and some of them blocks stylesheets from downloading. The easiest way to handle this is to work with inline styles (style="..."), but that is error prone and hard to work with as you cannot use classes and/or reuse styling over your HTML.

This gem makes this easier by automatically inlining stylesheets into the document. You give Roadie your CSS, or let it find it by itself from the <link> and <style> tags in the markup, and it will go through all of the selectors assigning the styles to the matching elements. Careful attention has been put into selectors being applied in the correct order, so it should behave just like in the browser.

"Dynamic" selectors (:hover, :visited, :focus, etc.), or selectors not understood by Nokogiri will be inlined into a single <style> element for those email clients that support it. This changes specificity a great deal for these rules, so it might not work 100% out of the box. (See more about this below)

Roadie also rewrites all relative URLs in the email to an absolute counterpart, making images you insert and those referenced in your stylesheets work. No more headaches about how to write the stylesheets while still having them work with emails from your acceptance environments. You can disable this on specific elements using a data-roadie-ignore marker.

Features

  • Writes CSS styles inline.
    • Respects !important styles.
    • Does not overwrite styles already present in the style attribute of tags.
    • Supports the same CSS selectors as Nokogiri; use CSS3 selectors in your emails!
    • Keeps :hover, @media { ... } and friends around in a separate <style> element.
  • Makes image urls absolute.
    • Hostname and port configurable on a per-environment basis.
    • Can be disabled on individual elements.
  • Makes link hrefs and img srcs absolute.
  • Automatically adds proper HTML skeleton when missing; you don't have to create a layout for emails.
    • Also supports HTML fragments / partial documents, where layout is not added.
  • Allows you to inject stylesheets in a number of ways, at runtime.
  • Removes data-roadie-ignore markers before finishing the HTML.

Install & Usage

Add this gem to your Gemfile as recommended by Rubygems and run bundle install.

gem 'roadie', '~> 5.2'

You can then create a new instance of a Roadie document:

# Transform full documents with the #transform method.
document = Roadie::Document.new "<html><body></body></html>"
document.add_css "body { color: green; }"
document.transform
    # => "<html><body style=\"color:green;\"></body></html>"

# Transform partial documents with #transform_partial.
document = Roadie::Document.new "<div>Hello world!</div>"
document.add_css "div { color: green; }"
document.transform_partial
    # => "<div style=\"color:green;\">Hello world!</div>"

Your document instance can be configured with several options:

  • url_options - Dictates how absolute URLs should be built.
  • keep_uninlinable_css - Set to false to skip CSS that cannot be inlined.
  • merge_media_queries - Set to false to not group media queries. Some users might prefer to not group rules within media queries because it will result in rules getting reordered. e.g.
    @media(max-width: 600px) { .col-6 { display: block; } }
    @media(max-width: 400px) { .col-12 { display: inline-block; } }
    @media(max-width: 600px) { .col-12 { display: block; } }
    will become
    @media(max-width: 600px) { .col-6 { display: block; } .col-12 { display: block; } }
    @media(max-width: 400px) { .col-12 { display: inline-block; } }
  • asset_providers - A list of asset providers that are invoked when CSS files are referenced. See below.
  • external_asset_providers - A list of asset providers that are invoked when absolute CSS URLs are referenced. See below.
  • before_transformation - A callback run before transformation starts.
  • after_transformation - A callback run after transformation is completed.
  • serialization_options - An integer bitmap of options passed along to Nokogiri during serialization. By default it's Nokogiri::XML::Node::SaveOptions::NO_DECLARATION | Nokogiri::XML::Node::SaveOptions::NO_EMPTY_TAGS.

Making URLs absolute

In order to make URLs absolute you need to first configure the URL options of the document.

html = '... <a href="/about-us">Read more!</a> ...'
document = Roadie::Document.new html
document.url_options = {host: "myapp.com", protocol: "https"}
document.transform
  # => "... <a href=\"https://myapp.com/about-us\">Read more!</a> ..."

The following URLs will be rewritten for you:

  • a[href] (HTML)
  • img[src] (HTML)
  • url() (CSS)

You can disable individual elements by adding an data-roadie-ignore marker on them. CSS will still be inlined on those elements, but URLs will not be rewritten.

<a href="|UNSUBSCRIBE_URL|" data-roadie-ignore>Unsubscribe</a>

Referenced stylesheets

By default, style and link elements in the email document's head are processed along with the stylesheets and removed from the head.

You can set a special data-roadie-ignore attribute on style and link tags that you want to ignore (the attribute will be removed, however). This is the place to put things like :hover selectors that you want to have for email clients allowing them.

Style and link elements with media="print" are also ignored.

<head>
  <link rel="stylesheet" type="text/css" href="/assets/emails/rock.css">         <!-- Will be inlined with normal providers -->
  <link rel="stylesheet" type="text/css" href="http://www.metal.org/metal.css">  <!-- Will be inlined with external providers, *IF* specified; otherwise ignored. -->
  <link rel="stylesheet" type="text/css" href="/assets/jazz.css" media="print">  <!-- Will NOT be inlined; print style -->
  <link rel="stylesheet" type="text/css" href="/ambient.css" data-roadie-ignore> <!-- Will NOT be inlined; ignored -->
  <style></style>                    <!-- Will be inlined -->
  <style data-roadie-ignore></style> <!-- Will NOT be inlined; ignored -->
</head>

Roadie will use the given asset providers to look for the actual CSS that is referenced. If you don't change the default, it will use the Roadie::FilesystemProvider which looks for stylesheets on the filesystem, relative to the current working directory.

Example:

# /home/user/foo/stylesheets/primary.css
body { color: green; }

# /home/user/foo/script.rb
html = <<-HTML
<html>
  <head>
  <link rel="stylesheet" type="text/css" href="/stylesheets/primary.css">
  </head>
  <body>
  </body>
</html>
HTML

Dir.pwd # => "/home/user/foo"
document = Roadie::Document.new html
document.transform # =>
                   # <!DOCTYPE html>
                   # <html>
                   #   <head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></head>
                   #   <body style="color:green;"></body>
                   # </html>

If a referenced stylesheet cannot be found, the #transform method will raise an Roadie::CssNotFound error. If you instead want to ignore missing stylesheets, you can use the NullProvider.

Configuring providers

You can write your own providers if you need very specific behavior for your app, or you can use the built-in providers. Providers come in two groups: normal and external. Normal providers handle paths without host information (/style/foo.css) while external providers handle URLs with host information (//example.com/foo.css, localhost:3001/bar.css, and so on).

The default configuration is to not have any external providers configured, which will cause those referenced stylesheets to be ignored. Adding one or more providers for external assets causes all of them to be searched and inlined, so if you only want this to happen to specific stylesheets you need to add ignore markers to every other styleshheet (see above).

Included providers:

  • FilesystemProvider – Looks for files on the filesystem, relative to the given directory unless otherwise specified.
  • ProviderList – Wraps a list of other providers and searches them in order. The asset_providers setting is an instance of this. It behaves a lot like an array, so you can push, pop, shift and unshift to it.
  • NullProvider – Does not actually provide anything, it always finds empty stylesheets. Use this in tests or if you want to ignore stylesheets that cannot be found by your other providers (or if you want to force the other providers to never run).
  • NetHttpProvider – Downloads stylesheets using Net::HTTP. Can be given a whitelist of hosts to download from.
  • CachedProvider – Wraps another provider (or ProviderList) and caches responses inside the provided cache store.
  • PathRewriterProvider – Rewrites the passed path and then passes it on to another provider (or ProviderList).

If you want to search several locations on the filesystem, you can declare that:

document.asset_providers = [
  Roadie::FilesystemProvider.new(App.root.join("resources", "stylesheets")),
  Roadie::FilesystemProvider.new(App.root.join("system", "uploads", "stylesheets")),
]

NullProvider

If you want to ignore stylesheets that cannot be found instead of crashing, push the NullProvider to the end:

# Don't crash on missing assets
document.asset_providers << Roadie::NullProvider.new

# Don't download assets in tests
document.external_asset_providers.unshift Roadie::NullProvider.new

Note: This will cause the referenced stylesheet to be removed from the source code, so email client will never see it either.

NetHttpProvider

The NetHttpProvider will download the URLs that is is given using Ruby's standard Net::HTTP library.

You can give it a whitelist of hosts that downloads are allowed from:

document.external_asset_providers << Roadie::NetHttpProvider.new(
  whitelist: ["myapp.com", "assets.myapp.com", "cdn.cdnnetwork.co.jp"],
)
document.external_asset_providers << Roadie::NetHttpProvider.new # Allows every host

CachedProvider

You might want to cache providers from working several times. If you are sending several emails quickly from the same process, this might also save a lot of time on parsing the stylesheets if you use in-memory storage such as a hash.

You can wrap any other kind of providers with it, even a ProviderList:

document.external_asset_providers = Roadie::CachedProvider.new(document.external_asset_providers, my_cache)

If you don't pass a cache backend, it will use a normal Hash. The cache store must follow this protocol:

my_cache["key"] = some_stylesheet_instance # => #<Roadie::Stylesheet instance>
my_cache["key"]                            # => #<Roadie::Stylesheet instance>
my_cache["missing"]                        # => nil

Warning: The default Hash store will never be cleared, so make sure you don't allow the number of unique asset paths to grow too large in a single run. This is especially important if you run Roadie in a daemon that accepts arbritary documents, and/or if you use hash digests in your filenames. Making a new instance of CachedProvider will use a new Hash instance.

You can implement your own custom cache store by implementing the [] and []= methods.

class MyRoadieMemcacheStore
  def initialize(memcache)
    @memcache = memcache
  end

  def [](path)
    css = memcache.read("assets/#{path}/css")
    if css
      name = memcache.read("assets/#{path}/name") || "cached #{path}"
      Roadie::Stylesheet.new(name, css)
    end
  end

  def []=(path, stylesheet)
    memcache.write("assets/#{path}/css", stylesheet.to_s)
    memcache.write("assets/#{path}/name", stylesheet.name)
    stylesheet # You need to return the set Stylesheet
  end
end

document.external_asset_providers = Roadie::CachedProvider.new(
  document.external_asset_providers,
  MyRoadieMemcacheStore.new(MemcacheClient.instance)
)

If you are using Rspec, you can test your implementation by using the shared examples for the "roadie cache store" role:

require "roadie/rspec"

describe MyRoadieMemcacheStore do
  let(:memcache_client) { MemcacheClient.instance }
  subject { MyRoadieMemcacheStore.new(memcache_client) }

  it_behaves_like "roadie cache store" do
    before { memcache_client.clear }
  end
end

PathRewriterProvider

With this provider, you can rewrite the paths that are searched in order to more easily support another provider. Examples could include rewriting absolute URLs into something that can be found on the filesystem, or to access internal hosts instead of external ones.

filesystem = Roadie::FilesystemProvider.new("assets")
document.asset_providers << Roadie::PathRewriterProvider.new(filesystem) do |path|
  path.sub('stylesheets', 'css').downcase
end

document.external_asset_providers = Roadie::PathRewriterProvider.new(filesystem) do |url|
  if url =~ /myapp\.com/
    URI.parse(url).path.sub(%r{^/assets}, '')
  else
    url
  end
end

You can also wrap a list, for example to implement external_asset_providers by composing the normal asset_providers:

document.external_asset_providers =
  Roadie::PathRewriterProvider.new(document.asset_providers) do |url|
    URI.parse(url).path
  end

Writing your own provider

Writing your own provider is also easy. You need to provide:

  • #find_stylesheet(name), returning either a Roadie::Stylesheet or nil.
  • #find_stylesheet!(name), returning either a Roadie::Stylesheet or raising Roadie::CssNotFound.
class UserAssetsProvider
  def initialize(user_collection)
    @user_collection = user_collection
  end

  def find_stylesheet(name)
    if name =~ %r{^/users/(\d+)\.css$}
      user = @user_collection.find_user($1)
      Roadie::Stylesheet.new("user #{user.id} stylesheet", user.stylesheet)
    end
  end

  def find_stylesheet!(name)
    find_stylesheet(name) or
      raise Roadie::CssNotFound.new(
        css_name: name, message: "does not match a user stylesheet", provider: self
      )
  end

  # Instead of implementing #find_stylesheet!, you could also:
  #     include Roadie::AssetProvider
  # That will give you a default implementation without any error message. If
  # you have multiple error cases, it's recommended that you implement
  # #find_stylesheet! without #find_stylesheet and raise with an explanatory
  # error message.
end

# Try to look for a user stylesheet first, then fall back to normal filesystem lookup.
document.asset_providers = [
  UserAssetsProvider.new(app),
  Roadie::FilesystemProvider.new('./stylesheets'),
]

You can test for compliance by using the built-in RSpec examples:

require 'spec_helper'
require 'roadie/rspec'

describe MyOwnProvider do
  # Will use the default `subject` (MyOwnProvider.new)
  it_behaves_like "roadie asset provider", valid_name: "found.css", invalid_name: "does_not_exist.css"

  # Extra setup just for these tests:
  it_behaves_like "roadie asset provider", valid_name: "found.css", invalid_name: "does_not_exist.css" do
    subject { MyOwnProvider.new(...) }
    before { stub_dependencies }
  end
end

Keeping CSS that is impossible to inline

Some CSS is impossible to inline properly. :hover and ::after comes to mind. Roadie tries its best to keep these around by injecting them inside a new <style> element in the <head> (or at the beginning of the partial if transforming a partial document).

The problem here is that Roadie cannot possible adjust the specificity for you, so they will not apply the same way as they did before the styles were inlined.

Another caveat is that a lot of email clients does not support this (which is the entire point of inlining in the first place), so don't put anything important in here. Always handle the case of these selectors not being part of the email.

Specificity problems

Inlined styles will have much higher specificity than styles in a <style>. Here's an example:

<style>p:hover { color: blue; }</style>
<p style="color: green;">Hello world</p>

When hovering over this <p>, the color will not change as the color: green rule takes precedence. You can get it to work by adding !important to the :hover rule.

It would be foolish to try to automatically inject !important on every rule automatically, so this is a manual process.

Turning it off

If you'd rather skip this and have the styles not possible to inline disappear, you can turn off this feature by setting the keep_uninlinable_css option to false.

document.keep_uninlinable_css = false

Callbacks

Callbacks allow you to do custom work on documents before they are transformed. The Nokogiri document tree is passed to the callable along with the Roadie::Document instance:

class TrackNewsletterLinks
  def call(dom, document)
    dom.css("a").each { |link| fix_link(link) }
  end

  def fix_link(link)
    divider = (link['href'] =~ /?/ ? '&' : '?')
    link['href'] = link['href'] + divider + 'source=newsletter'
  end
end

document.before_transformation = ->(dom, document) {
  logger.debug "Inlining document with title #{dom.at_css('head > title').try(:text)}"
}
document.after_transformation = TrackNewsletterLinks.new

XHTML vs HTML

You can configure the underlying HTML/XML engine to output XHTML or HTML (which is the default). One usecase for this is that { tokens usually gets escaped to &#123;, which would be a problem if you then pass the resulting HTML on to some other templating engine that uses those tokens (like Handlebars or Mustache).

document.mode = :xhtml

This will also affect the emitted <!DOCTYPE> if transforming a full document. Partial documents does not have a <!DOCTYPE>.

Build Status

Tested with Github CI using:
See Workflow Matrix

Let me know if you want any other runtime supported officially.

Versioning

This project follows Semantic Versioning and has been since version 1.0.0.

FAQ

Why is my markup changed in subtle ways?

Roadie uses Nokogiri to parse and regenerate the HTML of your email, which means that some unintentional changes might show up.

One example would be that Nokogiri might remove your &nbsp;s in some cases.

Another example is Nokogiri's lack of HTML5 support, so certain new element might have spaces removed. I recommend you don't use HTML5 in emails anyway because of bad email client support (that includes web mail!).

I'm getting segmentation faults (or other C-like problems)! What should I do?

Roadie uses Nokogiri to parse the HTML of your email, so any C-like problems like segfaults are likely in that end. The best way to fix this is to first upgrade libxml2 on your system and then reinstall Nokogiri. Instructions on how to do this on most platforms, see Nokogiri's official install guide.

What happened to my @keyframes?

The CSS Parser used in Roadie does not handle keyframes. I don't think any email clients do either, but if you want to keep on trying you can add them manually to a <style> element (or a separate referenced stylesheet) and tell Roadie not to touch them.

My @media queries are reordered, how can I fix this?

Different @media query blocks with the same conditions are merged by default, which will change the order in some cases. You can disable this by setting merge_media_queries to false. (See Install & Usage section above).

How do I get rid of the <body> elements that are added?

It sounds like you want to transform a partial document. Maybe you are building partials or template fragments to later place in other documents. Use Document#transform_partial instead of Document#transform in order to treat the HTML as a partial document.

Can I skip URL rewriting on a specific element?

If you add the data-roadie-ignore attribute on an element, URL rewriting will not be performed on that element. This could be really useful for you if you intend to send the email through some other rendering pipeline that replaces some placeholders/variables.

<a href="/about-us">About us</a>
<a href="|UNSUBSCRIBE_URL|" data-roadie-ignore>Unsubscribe</a>

Note that this will not skip CSS inlining on the element; it will still get the correct styles applied.

What should I do about "Invalid URL" errors?

If the URL is invalid on purpose, see Can I skip URL rewriting on a specific element? above. Otherwise, you can try to parse it yourself using Ruby's URI class and see if you can figure it out.

require "uri"
URI.parse("https://example.com/best image.jpg") # raises
URI.parse("https://example.com/best%20image.jpg") # Works!

Documentation

Running specs

bundle install
rake

Security

Roadie is set up with the assumption that all CSS and HTML passing through it is under your control. It is not recommended to run arbritary HTML with the default settings.

Care has been given to try to secure all file system accesses, but it is never guaranteed that someone cannot access something they should not be able to access.

In order to secure Roadie against file system access, only use your own asset providers that you yourself can secure against your particular environment.

If you have found any security vulnerability, please email me at [email protected] to disclose it. For very sensitive issues, please use my public GPG key. You can also encrypt your message with my public key and open an issue if you do not want to email me directly. Thank you.

History and contributors

This gem was previously tied to Rails. It is now framework-agnostic and supports any type of HTML documents. If you want to use it with Rails, check out roadie-rails.

Major contributors to Roadie:

You can see all contributors on GitHub.

License

(The MIT License)

Copyright (c) 2009-2022 Magnus Bergmark, Jim Neath / Purify, and contributors.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

roadie's People

Contributors

adamkiczula avatar afeld avatar alexandrebini avatar binarylogic avatar crystalneth avatar dbachet avatar dmarkow avatar greysteil avatar grimen avatar harrykiselev avatar ihoka avatar jeremy avatar jeznag avatar jimneath avatar kovalevsky avatar mange avatar mjonuschat avatar nickshatilo avatar opsidao avatar petergoldstein avatar phlipper avatar pikachuexe avatar qbart avatar robzolkos avatar romanbsd avatar saks avatar tricknotes avatar tylerhunt avatar zdraganov avatar zhaocai 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

roadie's Issues

Rails 3.1 Using links to anchor tags on same page.

I am not sure how to do this with roadie. The anchored element that I define with id = "post_1" does not get the id (looks like id attribute is stripped off). The anchor link itself gets mangled too so "/#post_1" becomes "/#13ad73a90f9c43ca_post_1".

Any help on how I can solve this will be much appreciated.

Absolute URLs

config.action_mailer.default_url_options - Used for making URLs absolute

Are relative paths possible?

Webfont rendering using @font-face

Hi,

I'm trying to get web-fonts to work with roadie. I know that they are not universally supported in email clients, but want to get it working for those clients that I can get.

I have tried the @font_face method, which is supposed to work in Apple Mail, but am unable to get it working.

@font-face {
  font-family: 'DINNextLTPro-Regular';
  src: url(http://localhost:3000/fonts/dinnextltproregular.ttf);
  font-weight: normal;
  font-style: normal;
}

Am i doing something wrong? Or is this not supported?

css parser chokes on @ type selectors

For instance, the Twitter's bootstrap.css has the following selector:

@-webkit-keyframes progress-bar-stripes {
  from {
    background-position: 40px 0;
  }
  to {
    background-position: 0 0;
  }
}

the workaround I have right now is the following:

--- a/lib/roadie/inliner.rb
+++ b/lib/roadie/inliner.rb
@@ -143,7 +143,7 @@ module Roadie
       end

       def each_selector_without_psuedo(rules)
-        rules.selectors.reject { |selector| selector.include?(':') }.each do |selector|
+        rules.selectors.reject { |selector| selector.include?(':') or selector.starts_with?('@') }.each do |selector|
           yield selector, CssParser.calculate_specificity(selector)
         end
       end

Doesn't use assets pipeline in Rails 4.0.0.rc1

config.assets.enabled now returns nil in Rails 4.0.0.rc1, as it has been deprecated.

Because of this, the current_provider method in lib/roadie.rb will always use FilesystemProvider instead of AssetPipelineProvder.

I should be able to submit a pull request for this soon, but wanted to report the issue in the mean time.

Invalid css header in production

Only in production, I'm getting an invalid email header of "css". It's being set to "email", which presumably comes from my code:

default css: ["email"]

I tracked this down by setting the delivery method for emails to :file. It's not a problem in test, with delivery_method :test. This header causes Amazon's SES to reject the email with:

postfix/smtp[19321]: 5C92742976: to=<[email protected]>, relay=email-smtp.us-east-1.amazonaws.com[XXX.XXX.XXX.XXX]:587, delay=1.5, delays=0.06/0.12/0.8/0.5, dsn=5.0.0, status=bounced (host email-smtp.us-east-1.amazonaws.com[XXX.XXX.XXX.XXX] said: 554 Transaction failed: Illegal header 'css'. (in reply to end of DATA command))

I'm using commit 4d98009. I'm using Rails 4.0.0.rc2 with Ruby 2.0.0-p0, and in case it matters, the stylus gem (using the rails_4 branch).

Use HTML instead of XHTML

XHTML isn't understood by most email clients. Roadie should not add XHTML boilerplate when missing!

Automatic HTML to text part conversion

I like the roadie gem, and it would be nice if it could also automatically create a text version of my emails for me (a la premailer-rails3). Thoughts?

Absolute urls for <a> tags

A section of my email comes from a field stored in the database. The <a> tags in this text have relative urls. Same database is shared across multiple instances and each server is supposed to serve the correct absolute URL.

The roadie gem doesn't perform the href absolute URL conversion for <a> tags.
Is there a way to enable this feature?

Ideally, I am looking for a way to register a custom adjuster for the email HTML.

config.roadie.custom_adjuster = lambda{ |document|      
  document.css("user-comments a").each do |link|
    link['href'] = ensure_absolute_url(link['href']) if img['href']
  end
}

Support for HAML templates

When passing a HAML template as a mailer, Roadie does not inlay styles correctly. For some reason, it adds a second equals sign = and then that sign is URL encoded at some point. As a result, styles appear like this:

<div style=3D"(styles)">

The styles are then (obviously) not rendered as this is not valid HTML.

In Rails 4, setting `css` option to a symbol calls method

I used to have this:

default css: :mailer

But in rails 4 it will be be considered as a method name instead
Because the code is changed in actionmailer

Rails 3:
https://github.com/rails/rails/blob/3-2-stable/actionmailer/lib/action_mailer/base.rb#L630

Rails 4
https://github.com/rails/rails/blob/master/actionmailer/lib/action_mailer/base.rb#L673

So in README you can specify that string should be used for CSS filename
And symbol is used for method name

3.1

Feature release with some nice features from the wishlists.

CSS3 styling is ignored

I'm using compass/sass on a rails project and the css3 stylings (ie: border-radius, box-shadow) are not being added to the inline styles. Am I missing something?

Example css output:

.goal_stats table tr td div {
  background: #41a0cf;
  border: 1px solid #1f88bd;
  -moz-border-radius: 2px;
  -webkit-border-radius: 2px;
  -o-border-radius: 2px;
  -ms-border-radius: 2px;
  -khtml-border-radius: 2px;
  border-radius: 2px;
  color: white;
  font-size: 11px;
  margin: 0 7px;
  padding: 15px;
  text-transform: uppercase;
  -moz-box-shadow: #62b0d7 1px 1px 1px 1px inset, #aaaaaa 1px 1px 3px 0;
  -webkit-box-shadow: #62b0d7 1px 1px 1px 1px inset, #aaaaaa 1px 1px 3px 0;
  -o-box-shadow: #62b0d7 1px 1px 1px 1px inset, #aaaaaa 1px 1px 3px 0;
  box-shadow: #62b0d7 1px 1px 1px 1px inset, #aaaaaa 1px 1px 3px 0;
}

Corresponding output in Gmail (Chrome):

<div style="font:inherit;vertical-align:baseline;border:1px solid #1f88bd;background:#41a0cf;color:white;font-size:11px;margin:0 7px;padding:15px;text-transform:uppercase">
              <strong style="margin:0;padding:0;border:0;vertical-align:baseline;font:inherit;display:block;font-size:44px;margin-bottom:15px">81 Days</strong>
              Average Goal Duration
            </div>

Environment info:
rails 3.0.10
ruby-1.9.2-p290
roadie 1.1.0

Image absolute URL breaks in certain situations

The following code, when rendered, removes the dot from the image filename (becomes communitypng).

= link_to(user_groups_url) do
  = image_tag('mailer/nav/community.png')

- # also breaks
= link_to(image_tag('mailer/nav/community.png'), user_groups_url)

But these variations work:

%a{:href => user_groups_url}
  = image_tag('mailer/nav/community.png')

- # doesn't get rewritten as absolute url though
= link_to(user_groups_path) do
  = image_tag('mailer/nav/community.png')

Oddly, some virtually identical code above it works, eg:

= link_to(recently_added_user_galleries_url) do
  = image_tag('mailer/nav/galleries.png')

Work with sass/compass precompiled assets

Roadie doesn't seem to use our precompiled email.css file, and instead defaults to the SASS file, which then fails.

I added roadie to our gemfile, and didn't change any configuration.

I set up our mailer.rb:

class Mailer < ActionMailer::Base
  default css: :email
  ...
end

And I added to our application.rb:

require File.expand_path('../boot', __FILE__)

require 'rails/all'

if defined?(Bundler)
  # If you precompile assets before deploying to production, use this line
  Bundler.require(*Rails.groups(:assets => %w(development test)))
  # If you want your assets lazily compiled in production, use this line
  # Bundler.require(:default, :assets, Rails.env)
end

module MyApp
  class Application < Rails::Application
    ...
    config.assets.precompile += ['email.css' ]
  end
end

But when I do an action on the site that triggers an email, I get Sass::SyntaxError: File to import not found or unreadable: compass. More:

{File to import not found or unreadable: compass.
Load paths:
  /app
  /app/vendor/bundle/ruby/1.9.1/gems/activeadmin-0.4.0/app/assets/stylesheets
  (in /app/app/assets/stylesheets/email.css.scss)
  /app/app/assets/stylesheets/email.css.scss:1
/app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/import_node.rb:64:in `rescue in import'
/app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/import_node.rb:42:in `import'
/app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/import_node.rb:25:in `imported_file'
/app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/perform.rb:149:in `rescue in visit_import'
/app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/perform.rb:154:in `visit_import'
/app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/base.rb:37:in `visit'
/app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/perform.rb:18:in `visit'
/app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/base.rb:53:in `block in visit_children'
/app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/base.rb:53:in `map'
/app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/base.rb:53:in `visit_children'
/app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/perform.rb:27:in `block in visit_children'
/app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/perform.rb:39:in `with_environment'
/app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/perform.rb:26:in `visit_children'
/app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/base.rb:37:in `block in visit'
/app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/perform.rb:47:in `visit_root'
/app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/base.rb:37:in `visit'
/app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/perform.rb:18:in `visit'
/app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/perform.rb:7:in `visit'
/app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/root_node.rb:20:in `render'
/app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/engine.rb:299:in `_render'
/app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/engine.rb:246:in `render'
/app/vendor/bundle/ruby/1.9.1/gems/tilt-1.3.3/lib/tilt/css.rb:24:in `evaluate'
/app/vendor/bundle/ruby/1.9.1/gems/tilt-1.3.3/lib/tilt/template.rb:76:in `render'
/app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/context.rb:193:in `block in evaluate'
/app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/context.rb:190:in `each'
/app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/context.rb:190:in `evaluate'
/app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/processed_asset.rb:12:in `initialize'
/app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/base.rb:249:in `new'
/app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/base.rb:249:in `block in build_asset'
/app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/base.rb:270:in `circular_call_protection'
/app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/base.rb:248:in `build_asset'
/app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/index.rb:93:in `block in build_asset'
/app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/caching.rb:19:in `cache_asset'
/app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/index.rb:92:in `build_asset'
/app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/base.rb:169:in `find_asset'
/app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/index.rb:60:in `find_asset'
/app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/bundled_asset.rb:16:in `initialize'
/app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/base.rb:252:in `new'
/app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/base.rb:252:in `build_asset'
/app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/index.rb:93:in `block in build_asset'
/app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/caching.rb:19:in `cache_asset'
/app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/index.rb:92:in `build_asset'
/app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/base.rb:169:in `find_asset'
/app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/index.rb:60:in `find_asset'
/app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/base.rb:177:in `[]'
/app/vendor/bundle/ruby/1.9.1/gems/roadie-2.3.4/lib/roadie/asset_pipeline_provider.rb:23:in `asset_file'
/app/vendor/bundle/ruby/1.9.1/gems/roadie-2.3.4/lib/roadie/asset_pipeline_provider.rb:13:in `find'
/app/vendor/bundle/ruby/1.9.1/gems/roadie-2.3.4/lib/roadie/asset_provider.rb:28:in `block in all'
/app/vendor/bundle/ruby/1.9.1/gems/roadie-2.3.4/lib/roadie/asset_provider.rb:28:in `map'
/app/vendor/bundle/ruby/1.9.1/gems/roadie-2.3.4/lib/roadie/asset_provider.rb:28:in `all'
/app/vendor/bundle/ruby/1.9.1/gems/roadie-2.3.4/lib/roadie/inliner.rb:34:in `initialize'
/app/vendor/bundle/ruby/1.9.1/gems/roadie-2.3.4/lib/roadie.rb:6:in `new'
/app/vendor/bundle/ruby/1.9.1/gems/roadie-2.3.4/lib/roadie.rb:6:in `inline_css'
/app/vendor/bundle/ruby/1.9.1/gems/roadie-2.3.4/lib/roadie/action_mailer_extensions.rb:46:in `inline_style_response'
/app/vendor/bundle/ruby/1.9.1/gems/roadie-2.3.4/lib/roadie/action_mailer_extensions.rb:33:in `block in collect_responses_and_parts_order_with_inline_styles'
/app/vendor/bundle/ruby/1.9.1/gems/roadie-2.3.4/lib/roadie/action_mailer_extensions.rb:33:in `map'
/app/vendor/bundle/ruby/1.9.1/gems/roadie-2.3.4/lib/roadie/action_mailer_extensions.rb:33:in `collect_responses_and_parts_order_with_inline_styles'
/app/vendor/bundle/ruby/1.9.1/gems/actionmailer-3.2.11/lib/action_mailer/base.rb:648:in `mail'
/app/vendor/bundle/ruby/1.9.1/gems/roadie-2.3.4/lib/roadie/action_mailer_extensions.rb:25:in `mail_with_inline_styles'
/app/app/mailers/mailer.rb:247:in `client_books_coach'
/app/vendor/bundle/ruby/1.9.1/gems/actionpack-3.2.11/lib/abstract_controller/base.rb:167:in `process_action'
/app/vendor/bundle/ruby/1.9.1/gems/actionpack-3.2.11/lib/abstract_controller/base.rb:121:in `process'
/app/vendor/bundle/ruby/1.9.1/gems/actionpack-3.2.11/lib/abstract_controller/rendering.rb:45:in `process'
/app/vendor/bundle/ruby/1.9.1/gems/actionmailer-3.2.11/lib/action_mailer/base.rb:458:in `process'
/app/vendor/bundle/ruby/1.9.1/gems/actionmailer-3.2.11/lib/action_mailer/base.rb:452:in `initialize'
/app/vendor/bundle/ruby/1.9.1/gems/actionmailer-3.2.11/lib/action_mailer/base.rb:439:in `new'
/app/vendor/bundle/ruby/1.9.1/gems/actionmailer-3.2.11/lib/action_mailer/base.rb:439:in `method_missing'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/performable_mailer.rb:6:in `perform'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/backend/base.rb:94:in `block in invoke_job'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:60:in `call'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:60:in `block in initialize'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:65:in `call'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:65:in `execute'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:38:in `run_callbacks'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/backend/base.rb:91:in `invoke_job'
(eval):3:in `block in invoke_job_with_newrelic_transaction_trace'
/app/vendor/bundle/ruby/1.9.1/gems/newrelic_rpm-3.5.3.25/lib/new_relic/agent/instrumentation/controller_instrumentation.rb:268:in `block in perform_action_with_newrelic_trace'
/app/vendor/bundle/ruby/1.9.1/gems/newrelic_rpm-3.5.3.25/lib/new_relic/agent/method_tracer.rb:242:in `trace_execution_scoped'
/app/vendor/bundle/ruby/1.9.1/gems/newrelic_rpm-3.5.3.25/lib/new_relic/agent/instrumentation/controller_instrumentation.rb:263:in `perform_action_with_newrelic_trace'
(eval):2:in `invoke_job_with_newrelic_transaction_trace'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/worker.rb:182:in `block (2 levels) in run'
/app/vendor/ruby-1.9.3/lib/ruby/1.9.1/timeout.rb:68:in `timeout'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/worker.rb:182:in `block in run'
/app/vendor/ruby-1.9.3/lib/ruby/1.9.1/benchmark.rb:295:in `realtime'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/worker.rb:181:in `run'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/worker.rb:238:in `block in reserve_and_run_one_job'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:60:in `call'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:60:in `block in initialize'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:65:in `call'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:65:in `execute'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:38:in `run_callbacks'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/worker.rb:238:in `reserve_and_run_one_job'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/worker.rb:166:in `block in work_off'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/worker.rb:165:in `times'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/worker.rb:165:in `work_off'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/worker.rb:133:in `block (4 levels) in start'
/app/vendor/ruby-1.9.3/lib/ruby/1.9.1/benchmark.rb:295:in `realtime'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/worker.rb:132:in `block (3 levels) in start'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:60:in `call'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:60:in `block in initialize'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:65:in `call'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:65:in `execute'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:38:in `run_callbacks'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/worker.rb:129:in `block (2 levels) in start'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/worker.rb:128:in `loop'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/worker.rb:128:in `block in start'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/plugins/clear_locks.rb:7:in `call'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/plugins/clear_locks.rb:7:in `block (2 levels) in <class:ClearLocks>'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:78:in `call'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:78:in `block (2 levels) in add'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:60:in `call'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:60:in `block in initialize'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:78:in `call'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:78:in `block in add'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:65:in `call'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:65:in `execute'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/lifecycle.rb:38:in `run_callbacks'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/worker.rb:127:in `start'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.3/lib/delayed/tasks.rb:9:in `block (2 levels) in <top (required)>'
/app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.3/lib/rake/task.rb:228:in `call'
/app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.3/lib/rake/task.rb:228:in `block in execute'
/app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.3/lib/rake/task.rb:223:in `each'
/app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.3/lib/rake/task.rb:223:in `execute'
/app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.3/lib/rake/task.rb:166:in `block in invoke_with_call_chain'
/app/vendor/ruby-1.9.3/lib/ruby/1.9.1/monitor.rb:211:in `mon_synchronize'
/app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.3/lib/rake/task.rb:159:in `invoke_with_call_chain'
/app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.3/lib/rake/task.rb:152:in `invoke'
/app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.3/lib/rake/application.rb:143:in `invoke_task'
/app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.3/lib/rake/application.rb:101:in `block (2 levels) in top_level'
/app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.3/lib/rake/application.rb:101:in `each'
/app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.3/lib/rake/application.rb:101:in `block in top_level'
/app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.3/lib/rake/application.rb:110:in `run_with_threads'
/app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.3/lib/rake/application.rb:95:in `top_level'
/app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.3/lib/rake/application.rb:73:in `block in run'
/app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.3/lib/rake/application.rb:160:in `standard_exception_handling'
/app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.3/lib/rake/application.rb:70:in `run'
/app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.3/bin/rake:33:in `<top (required)>'
/app/vendor/bundle/ruby/1.9.1/bin/rake:23:in `load'
/app/vendor/bundle/ruby/1.9.1/bin/rake:23:in `<main>'"

We're hosted on heroku, and all our assets are precompiled on the push to heroku and then served statically. Just as an experiment I added a `<%= stylesheet_link_tag 'email.css' %> to our application layout, and it was included in the page just fine.

When logged into the console I get this:

irb(main)> MyApp::Application.assets['email.css']
Sass::SyntaxError: File to import not found or unreadable: compass.
Load paths:
  /app
  /app/vendor/bundle/ruby/1.9.1/gems/activeadmin-0.4.0/app/assets/stylesheets
    from /app/app/assets/stylesheets/email.css.scss:1
    from /app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/import_node.rb:64:in `rescue in import'
    from /app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/import_node.rb:42:in `import'
    from /app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/import_node.rb:25:in `imported_file'
    from /app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/perform.rb:149:in `rescue in visit_import'
    from /app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/perform.rb:154:in `visit_import'
    from /app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/base.rb:37:in `visit'
    from /app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/perform.rb:18:in `visit'
    from /app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/base.rb:53:in `block in visit_children'
    from /app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/base.rb:53:in `map'
    from /app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/base.rb:53:in `visit_children'
    from /app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/perform.rb:27:in `block in visit_children'
    from /app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/perform.rb:39:in `with_environment'
    from /app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/perform.rb:26:in `visit_children'
    from /app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/base.rb:37:in `block in visit'
    from /app/vendor/bundle/ruby/1.9.1/gems/sass-3.1.15/lib/sass/tree/visitors/perform.rb:47:in `visit_root'
... 20 levels...
    from /app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/index.rb:60:in `find_asset'
    from /app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/bundled_asset.rb:16:in `initialize'
    from /app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/base.rb:252:in `new'
    from /app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/base.rb:252:in `build_asset'
    from /app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/index.rb:93:in `block in build_asset'
    from /app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/caching.rb:19:in `cache_asset'
    from /app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/index.rb:92:in `build_asset'
    from /app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/base.rb:169:in `find_asset'
    from /app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/index.rb:60:in `find_asset'
    from /app/vendor/bundle/ruby/1.9.1/gems/sprockets-2.2.2/lib/sprockets/base.rb:177:in `[]'
    from (irb):12
    from /app/vendor/bundle/ruby/1.9.1/gems/railties-3.2.11/lib/rails/commands/console.rb:47:in `start'
    from /app/vendor/bundle/ruby/1.9.1/gems/railties-3.2.11/lib/rails/commands/console.rb:8:in `start'
    from /app/vendor/bundle/ruby/1.9.1/gems/railties-3.2.11/lib/rails/commands.rb:41:in `<top (required)>'
    from script/rails:6:in `require'
    from script/rails:6:in `<main>'

Selector with nth-child does not work

Rails 3.2.12, Roadie 2.3.4, nokogiri 1.5.6
I want alternative row background color effect
So I tried:

  .job-row:nth-child(2n) {
    background-color: $grey-lighter;
  }

  .job-row:nth-child(2n+1) {
    background-color: $grey-lighter;
  }

The code itself has no error.
I tested it by pasting the selector to inspector in Chrome
I am not sure if its the problem of roadie or one of dependencies

I am using this for the effect right now

.job-row(class: cycle('row-odd', 'row-even'))

s3 urls get an additional "." appended to their url

In my email layout I have a very simple image tag which points to amazon s3.

<img src="http://s3.amazonaws.com/bucket/image.jpg">

When roadie is install this url gets converted to this:

<img src="http://s3.amazonaws..com/bucket/image.jpg">

[Note the additional "." before com]

Roadie without Rails?

Have you considered supporting using roadie outside of Rails (but still using actionmailer)?

Know of anyone who has a recipe for this?

I'm trying to put something together now, I'll let you know what I come up with.

Using Rails 3.1 but with no Assets Pipeline

Hello, I am using Rails 3.1 but the assets pipeline is disabled, so Roadie fails like a boss because it's expecting it enabled. Any way to use it without the Assets Pipeline?

Handle CDATA and comments in CSS

Gist with failing spec: https://gist.github.com/3930767

Basically, if the included CSS is wrapped in CDATA and HTML comments (TinyMCE does that), Roadie chokes and spits out an unhelpful error message.

Nokogiri::CSS::SyntaxError (unexpected '<' after ''):
  app/mailers/doodad_mailer.rb:24:in `doodad' # location of the 'mail' method call
  app/models/doodad.rb:23:in `deliver_test'
  app/controllers/doodads_controller.rb:79:in `email'

Roadie can't find css resource when in production

Hello,

Unfortunately Roadie can not find:
Roadie::CSSFileNotFound: Could not find newsletter-b123fff9900e0cf90cbd81e2720a07bc.css

I have checked and this resource is where it should be:
public/assets/newsletter-b123fff9900e0cf90cbd81e2720a07bc.css

Any idea how can i fix it? :-)

Thanks in advance!

Roadie::CSSFileNotFound with Rails 3.2/roadie 2.3.0.pre1

This is similar to #15. I'm also seeing a Roadie:CSSFileNotFound exception with Rails 3.2 and Roadie 2.3.0.pre1. It was working for me with Rails 3.1 and Roadie 2.2.0. I tried the fix mentioned by maraczek in #15, but that didn't work for me.

The specific exception I'm getting is:

Roadie::CSSFileNotFound (Could not find email-49a1a20a64caf9a4b757cac2fe2733e6.css):
  app/controllers/identity/registrations_controller.rb:4:in `create'

I have verified the file exists and is in the correct location. This is my staging environment, which is configured exactly like a production environment. I'm deploying using Capistrano.

Any thoughts?

option to not remove tags

There are situations where I want to keep the tags despite them already been inlined, for example, in Hotmail it's possible to override some css classes that are not defined when the email is sent, only when it's read. If you inline them, those classes will be ignored. I think data immutability should just disable inlining.

How to use asset pipeline with data-immutable?

First, thanks for this gem, it's great for sending HTML emails that have to work in Gmail!

I was just wondering what's the best way to use the asset pipeline to create the CSS for responsive design, but then have it converted to a <style> tag when sending?

For example, I'd like to have something like this:

<link rel="stylesheet" type="text/css" href="email-common.css">
<link rel="stylesheet" type="text/css" href="email-responsive.css" data-immutable>

And have the first one inlined (which it is), but have the second one converted to a <style> block, so there's no external stylesheets to be loaded by the email client.

This is the best I've come up with, which is specific to the asset pipeline and won't work with the filesystem option. I don't yet know if it will even work in production!

<style type="text/css" data-immutable>
<%= Rails.application.assets['email-responsive.css'].to_s.strip %>
</style>

As far as I can see, Roadie can't do this automatically. Would you consider adding that as an option - e.g. another 'data-' parameter? If not, perhaps you could add something about it to the readme?

Thanks
Dave

strict dependence of nokogiri

Bundler could not find compatible versions for gem "nokogiri":
  In Gemfile:
    fog (~> 1.12.1) ruby depends on
      nokogiri (~> 1.5.0) ruby

    roadie (>= 0) ruby depends on
      nokogiri (1.6.0)

Seems to not be maintaining !important styles?

.orange { color: orange !important; } %p.orange it's imporant that this is orange to override client's stylesheet

However the style tag for the paragraph is being generated as color: orange; when it needs to be color: orange !imporant; How do I get the latter result?

Enabling/Disabling roadie per mailer/per mail method

I want to propose an enhancement to enable/disable roadie per mailer or per mailer method.

# application.rb.  
config.roadie.enabled = true

class UserMailer < ActionMailer::Base
  # Disable roadie for all mails sent by UserMailer
  default :roadie_enabled => false
end

class AdminMailer < ActionMailer::Base
  def account_activation
    # Disable roadie for account_activation emails 
    mail(:roadie_enabled => false)
  end  

  def member_invitation
    # ...
  end    
end

This will allow us to enable roadie for specific emails.

Media queries

I have the following type of media query as part of my CSS, and currently Roadie is inlining it causing every single client to behave like a mobile mail client. I guess media queries should be ignored from inlining.

  @media only screen and (max-device-width: 480px) {
       div[class="header"] {
            font-size: 16px !important;
       }
       table[class="table"], td[class="cell"] {
            width: 300px !important;
       }
    table[class="promotable"], td[class="promocell"] {
            width: 325px !important;
       }
    td[class="footershow"] {
            width: 300px !important;
       }
    table[class="hide"], img[class="hide"], td[class="hide"] {
            display: none !important;
       }
       img[class="divider"] {
          height: 1px !important;
     }

Headers disappear.

Hi,

I'm using your gem but I've encountered a problem: When I try to set any special header field (in this case "X-SMTPAPI" because I use sendgrid) in my ActionMailer, it never makes it to the final mail. Without Roadie it works just fine. I've tried to step through the execution but it's rather lengthy. I'm using Rails 3.0.4 and Roadie 1.0.0.pre2.
Are there any other gems that I should be aware of that could potentially influence the mechanics of Roadie?

Regards,
Nicholas

Useless error message on invalid URLs in <link> elements

Roadie will emit the following error when an invalid URL is inside the href of a <link> element:

URI::InvalidURIError
bad URI(is not URI?):

This should be more informative for the user, perhaps:

Roadie::Error Invalid URL inside <link> href: ""

Rails 4.0.0.rc1 error: undefined method `collect_responses_and_parts_order'

When trying to run unicorn this is what I get:

E, [2013-05-30T17:13:09.272125 #6172] ERROR -- : undefined method `collect_responses_and_parts_order' for class `ActionMailer::Base' (NameError)
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/activesupport-4.0.0.rc1/lib/active_support/core_ext/module/aliasing.rb:32:in `alias_method'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/activesupport-4.0.0.rc1/lib/active_support/core_ext/module/aliasing.rb:32:in `alias_method_chain'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/roadie-1.1.1/lib/roadie/action_mailer_extensions.rb:12:in `block in included'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/roadie-1.1.1/lib/roadie/action_mailer_extensions.rb:11:in `class_eval'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/roadie-1.1.1/lib/roadie/action_mailer_extensions.rb:11:in `included'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/roadie-1.1.1/lib/roadie.rb:38:in `include'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/roadie-1.1.1/lib/roadie.rb:38:in `<top (required)>'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/bundler-1.3.0/lib/bundler/runtime.rb:72:in `require'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/bundler-1.3.0/lib/bundler/runtime.rb:72:in `block (2 levels) in require'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/bundler-1.3.0/lib/bundler/runtime.rb:70:in `each'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/bundler-1.3.0/lib/bundler/runtime.rb:70:in `block in require'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/bundler-1.3.0/lib/bundler/runtime.rb:59:in `each'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/bundler-1.3.0/lib/bundler/runtime.rb:59:in `require'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/bundler-1.3.0/lib/bundler.rb:132:in `require'
/Users/Tomaz/Projects/Codeable/config/application.rb:5:in `<top (required)>'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:45:in `require'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:45:in `require'
/Users/Tomaz/Projects/Codeable/config/environment.rb:2:in `<top (required)>'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:45:in `require'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:45:in `require'
config.ru:4:in `block in <main>'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rack-1.5.2/lib/rack/builder.rb:55:in `instance_eval'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/rack-1.5.2/lib/rack/builder.rb:55:in `initialize'
config.ru:1:in `new'
config.ru:1:in `<main>'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/unicorn-4.6.2/lib/unicorn.rb:44:in `eval'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/unicorn-4.6.2/lib/unicorn.rb:44:in `block in builder'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/unicorn-4.6.2/lib/unicorn/http_server.rb:722:in `call'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/unicorn-4.6.2/lib/unicorn/http_server.rb:722:in `build_app!'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/unicorn-4.6.2/lib/unicorn/http_server.rb:595:in `init_worker_process'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/unicorn-4.6.2/lib/unicorn/http_server.rb:615:in `worker_loop'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/unicorn-4.6.2/lib/unicorn/http_server.rb:500:in `spawn_missing_workers'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/unicorn-4.6.2/lib/unicorn/http_server.rb:511:in `maintain_worker_count'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/unicorn-4.6.2/lib/unicorn/http_server.rb:277:in `join'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/gems/unicorn-4.6.2/bin/unicorn:126:in `<top (required)>'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/bin/unicorn:23:in `load'
/Users/Tomaz/.rbenv/versions/2.0.0-p0/bin/unicorn:23:in `<main>'
E, [2013-05-30T17:13:09.280855 #6147] ERROR -- : reaped #<Process::Status: pid 6172 exit 1> worker=0
I, [2013-05-30T17:13:09.281093 #6147]  INFO -- : worker=0 spawning...
I, [2013-05-30T17:13:09.283259 #6173]  INFO -- : worker=0 spawned pid=6173
I, [2013-05-30T17:13:09.283698 #6173]  INFO -- : Refreshing Gem list

Disable roadie on demand

Roadie is slow and if you want to show a preview of a message before sending it (and don't need inline styles since it's in-browser), it can really slow down the request (add a couple hundred of ms per mailer). Is there a way to disable roadie by request so the mailer previews don't inline the styles while the actual mailer (handled by Sidekiq) has inline styles?

Seg Fault

When I bundle the gem and try running rake (before doing any other set up) I get a seg fault. I'm using Lion 10.7.2, Rails 3.1.3, and Ruby 1.9.2p290. I am also using Devise 1.5.2, not sure if there is an incompatibility there but it shows up in my stacktrace.

The one part of the stacktrace that mentions roadie is:
/Users/.../gems/roadie-2.2.0/lib/roadie/action_mailer_extensions.rb:25:in `mail_with_inline_styles'

When I remove the roadie gem, the segfault goes away.

Here's a gist of the stacktrace: https://gist.github.com/1643793

undefined method `assets' for #<Rails::Application::Configuration:...>

From what I take, asset pipeline wasn't included until rails 3.1, this leads to a break on line #32:

def current_provider
  return config.roadie.provider if config.roadie.provider

  if config.assets.enabled ############# line 32 ##########
    AssetPipelineProvider.new
  else
    FilesystemProvider.new
  end
end

I changed it to:
if config.respond_to?(:assets) && config.assets.enabled

which solves my problem, but really .. the dependencies are broken :(

image-url() instead of url() in production

Hello!

First of all, thank you for this great gem! :)

But I have one strange issue:

Rails 3.2.3
Roadie 2.3.1

In production the mail contain style Tags with background:image-url(http...) instead of background:url(http...)

In development everything works fine.

The precompiled CSS file contains the correct background definition generated from SCSS .header{background:url(/assets...);}

I tried both techniques to include the CSS. A HTML Tag in the head and the :css option. The same problem with both.

Any ideas what this could be?

Disable URL escaping in anchor tags

I have been running into problems where Roadie appears to rewriting certain characters in urls in anchor tags. Specifically, I have run into the problem of "|" and "$" being escaped.

Would be nice if I could disable this behavior for certain links. Maybe skipping any would work well?

URI::InvalidComponentError in MailPreview

I am using mail_preview for email template preview
But sometimes (not always) it gives this error:

URI::InvalidComponentError - bad component(expected scheme component): http://:
  /usr/local/rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/uri/generic.rb:330:in `check_scheme'
  /usr/local/rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/uri/generic.rb:371:in `scheme='
  /usr/local/rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/uri/generic.rb:193:in `initialize'
  /usr/local/rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/uri/generic.rb:141:in `build'
  (gem) roadie-2.3.4/lib/roadie/inliner.rb:192:in `absolute_url_base'
  (gem) roadie-2.3.4/lib/roadie/inliner.rb:179:in `ensure_absolute_url'
  (gem) roadie-2.3.4/lib/roadie/inliner.rb:167:in `block in make_image_urls_absolute'
  (gem) nokogiri-1.5.9/lib/nokogiri/xml/node_set.rb:239:in `block in each'
  (gem) nokogiri-1.5.9/lib/nokogiri/xml/node_set.rb:238:in `each'
  (gem) roadie-2.3.4/lib/roadie/inliner.rb:166:in `make_image_urls_absolute'
  (gem) roadie-2.3.4/lib/roadie/inliner.rb:55:in `block in execute'
  (gem) roadie-2.3.4/lib/roadie/inliner.rb:77:in `block in adjust_html'
  (gem) roadie-2.3.4/lib/roadie/inliner.rb:76:in `adjust_html'
  (gem) roadie-2.3.4/lib/roadie/inliner.rb:49:in `execute'
  (gem) roadie-2.3.4/lib/roadie.rb:6:in `inline_css'
  (gem) roadie-2.3.4/lib/roadie/action_mailer_extensions.rb:46:in `inline_style_response'
  (gem) roadie-2.3.4/lib/roadie/action_mailer_extensions.rb:33:in `block in collect_responses_and_parts_order_with_inline_styles'
  (gem) roadie-2.3.4/lib/roadie/action_mailer_extensions.rb:33:in `collect_responses_and_parts_order_with_inline_styles'
  (gem) actionmailer-3.2.12/lib/action_mailer/base.rb:648:in `mail'
  (gem) roadie-2.3.4/lib/roadie/action_mailer_extensions.rb:25:in `mail_with_inline_styles'
  app/mailers/employer_mailer.rb:10:in `welcome'
  (gem) actionpack-3.2.12/lib/abstract_controller/base.rb:167:in `process_action'
  (gem) actionpack-3.2.12/lib/abstract_controller/base.rb:121:in `process'
  (gem) actionpack-3.2.12/lib/abstract_controller/rendering.rb:45:in `process'
  (gem) actionmailer-3.2.12/lib/action_mailer/base.rb:458:in `process'
  (gem) actionmailer-3.2.12/lib/action_mailer/base.rb:452:in `initialize'
  (gem) actionmailer-3.2.12/lib/action_mailer/base.rb:439:in `method_missing'
  app/mailers/mail_preview.rb:3:in `welcome_employer'
  (gem) mail_view-1.0.3/lib/mail_view.rb:38:in `call'
  (gem) mail_view-1.0.3/lib/mail_view.rb:19:in `call'
  (gem) journey-1.0.4/lib/journey/router.rb:68:in `block in call'
  (gem) journey-1.0.4/lib/journey/router.rb:56:in `call'
  (gem) actionpack-3.2.12/lib/action_dispatch/routing/route_set.rb:601:in `call'
  (gem) rack-pjax-0.7.0/lib/rack/pjax.rb:12:in `call'
  (gem) omniauth-1.1.4/lib/omniauth/strategy.rb:184:in `call!'
  (gem) omniauth-1.1.4/lib/omniauth/strategy.rb:164:in `call'
  (gem) omniauth-1.1.4/lib/omniauth/strategy.rb:184:in `call!'
  (gem) omniauth-1.1.4/lib/omniauth/strategy.rb:164:in `call'
  (gem) meta_request-0.2.3/lib/meta_request/middlewares/app_request_handler.rb:11:in `call'
  (gem) rack-contrib-1.1.0/lib/rack/contrib/response_headers.rb:17:in `call'
  (gem) meta_request-0.2.3/lib/meta_request/middlewares/headers.rb:16:in `call'
  (gem) meta_request-0.2.3/lib/meta_request/middlewares/meta_request_handler.rb:13:in `call'
  (gem) bullet-4.5.0/lib/bullet/rack.rb:11:in `call'
  (gem) newrelic_rpm-3.6.0.83/lib/new_relic/rack/error_collector.rb:12:in `call'
  (gem) newrelic_rpm-3.6.0.83/lib/new_relic/rack/agent_hooks.rb:18:in `call'
  (gem) newrelic_rpm-3.6.0.83/lib/new_relic/rack/browser_monitoring.rb:16:in `call'
  (gem) rack-google-analytics-0.11.0/lib/rack/google-analytics.rb:18:in `_call'
  (gem) rack-google-analytics-0.11.0/lib/rack/google-analytics.rb:15:in `call'
  (gem) warden-1.2.1/lib/warden/manager.rb:35:in `block in call'
  (gem) warden-1.2.1/lib/warden/manager.rb:34:in `call'
  (gem) actionpack-3.2.12/lib/action_dispatch/middleware/best_standards_support.rb:17:in `call'
  (gem) rack-1.4.5/lib/rack/etag.rb:23:in `call'
  (gem) rack-1.4.5/lib/rack/conditionalget.rb:25:in `call'
  (gem) actionpack-3.2.12/lib/action_dispatch/middleware/head.rb:14:in `call'
  (gem) remotipart-1.0.5/lib/remotipart/middleware.rb:30:in `call'
  (gem) actionpack-3.2.12/lib/action_dispatch/middleware/params_parser.rb:21:in `call'
  (gem) actionpack-3.2.12/lib/action_dispatch/middleware/flash.rb:242:in `call'
  (gem) rack-1.4.5/lib/rack/session/abstract/id.rb:210:in `context'
  (gem) rack-1.4.5/lib/rack/session/abstract/id.rb:205:in `call'
  (gem) actionpack-3.2.12/lib/action_dispatch/middleware/cookies.rb:341:in `call'
  (gem) activerecord-3.2.12/lib/active_record/query_cache.rb:64:in `call'
  (gem) activerecord-3.2.12/lib/active_record/connection_adapters/abstract/connection_pool.rb:479:in `call'
  (gem) actionpack-3.2.12/lib/action_dispatch/middleware/callbacks.rb:28:in `block in call'
  (gem) activesupport-3.2.12/lib/active_support/callbacks.rb:405:in `_run__415732725367540646__call__2241787683446459159__callbacks'
  (gem) activesupport-3.2.12/lib/active_support/callbacks.rb:405:in `__run_callback'
  (gem) activesupport-3.2.12/lib/active_support/callbacks.rb:385:in `_run_call_callbacks'
  (gem) activesupport-3.2.12/lib/active_support/callbacks.rb:81:in `run_callbacks'
  (gem) actionpack-3.2.12/lib/action_dispatch/middleware/callbacks.rb:27:in `call'
  (gem) actionpack-3.2.12/lib/action_dispatch/middleware/reloader.rb:65:in `call'
  (gem) actionpack-3.2.12/lib/action_dispatch/middleware/remote_ip.rb:31:in `call'
  (gem) airbrake-3.1.11/lib/airbrake/rails/middleware.rb:13:in `call'
  (gem) better_errors-0.8.0/lib/better_errors/middleware.rb:84:in `protected_app_call'
  (gem) better_errors-0.8.0/lib/better_errors/middleware.rb:79:in `better_errors_call'
  (gem) better_errors-0.8.0/lib/better_errors/middleware.rb:56:in `call'
  (gem) actionpack-3.2.12/lib/action_dispatch/middleware/debug_exceptions.rb:16:in `call'
  (gem) actionpack-3.2.12/lib/action_dispatch/middleware/show_exceptions.rb:56:in `call'
  (gem) railties-3.2.12/lib/rails/rack/logger.rb:32:in `call_app'
  (gem) railties-3.2.12/lib/rails/rack/logger.rb:16:in `block in call'
  (gem) activesupport-3.2.12/lib/active_support/tagged_logging.rb:22:in `tagged'
  (gem) railties-3.2.12/lib/rails/rack/logger.rb:16:in `call'
  (gem) quiet_assets-1.0.2/lib/quiet_assets.rb:18:in `call_with_quiet_assets'
  (gem) actionpack-3.2.12/lib/action_dispatch/middleware/request_id.rb:22:in `call'
  (gem) rack-1.4.5/lib/rack/methodoverride.rb:21:in `call'
  (gem) rack-1.4.5/lib/rack/runtime.rb:17:in `call'
  (gem) activesupport-3.2.12/lib/active_support/cache/strategy/local_cache.rb:72:in `call'
  (gem) rack-1.4.5/lib/rack/lock.rb:15:in `call'
  (gem) rack-livereload-0.3.13/lib/rack/livereload.rb:60:in `_call'
  (gem) rack-livereload-0.3.13/lib/rack/livereload.rb:51:in `call'
  (gem) actionpack-3.2.12/lib/action_dispatch/middleware/static.rb:62:in `call'
  (gem) airbrake-3.1.11/lib/airbrake/user_informer.rb:16:in `_call'
  (gem) airbrake-3.1.11/lib/airbrake/user_informer.rb:12:in `call'
  (gem) railties-3.2.12/lib/rails/engine.rb:479:in `call'
  (gem) railties-3.2.12/lib/rails/application.rb:223:in `call'
  (gem) railties-3.2.12/lib/rails/railtie/configurable.rb:30:in `method_missing'
  (gem) unicorn-4.6.2/lib/unicorn/http_server.rb:552:in `process_client'
  (gem) unicorn-4.6.2/lib/unicorn/http_server.rb:632:in `worker_loop'
  (gem) newrelic_rpm-3.6.0.83/lib/new_relic/agent/instrumentation/unicorn_instrumentation.rb:22:in `block (4 levels) in <top (required)>'
  (gem) unicorn-4.6.2/lib/unicorn/http_server.rb:500:in `spawn_missing_workers'
  (gem) unicorn-4.6.2/lib/unicorn/http_server.rb:142:in `start'
  (gem) unicorn-4.6.2/bin/unicorn_rails:209:in `<top (required)>'
  /usr/local/rvm/gems/ruby-2.0.0-p0/bin/unicorn_rails:23:in `<main>'
  /usr/local/rvm/gems/ruby-2.0.0-p0/bin/ruby_noexec_wrapper:14:in `<main>'

It is very strange that it only happen sometimes, not always

Option to leave styles in <head> (using Haml)

Great gem! One of the snags we ran into though was that there's no simple way to keep styles in the head (inline, not linked). For media queries, hover styles, and the like, it's nice that there's the option to separate them into their own stylesheets via the data-immutable parameter, but it's better practice to be able to leave styles in the head rather than have them linked externally.

Additionally, because we're using Haml, the :css filter doesn't allow us to pass 'data-immutable', and using the %style tag doesn't fly because nested styles will still be rendered as Haml (and spit errors).

I've currently settled on using the :plain filter to avoid nesting errors, but am wondering if there's a more elegant solution...

Roadie 2.4.1: Roadie got error when looking for...

I'm getting these errors when upgrading to 2.4.1 from 2.3.4. Specs still pass but the output is super verbose. I can't seem to find anything on this other than this issue which mentions the error but I think it's a different context? #49

Roadie got error when looking for ".container:after": xmlXPathCompOpEval: function after not found

Roadie got error when looking for ":-moz-placeholder": xmlXPathCompOpEval: function moz-placeholder not found

Roadie got error when looking for ":-ms-input-placeholder": xmlXPathCompOpEval: function ms-input-placeholder not found

.Roadie got error when looking for ".container:before": xmlXPathCompOpEval: function before not found

Server aborting

We're getting this error occasionally:

ruby(5772,0x102800000) malloc: *** error for object 0x8: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
zsh: abort      bundle exec rails s

It happens when executing this line:
compiled_html = Roadie.inline_css(Roadie.current_provider, %w(reports), html, {})

Meaning that If we remove that line, the crashes don't occur. It can happen doing the same request a couple of times.

URI::InvalidComponentError in rspec

When I am running my specs I get this error

URI::InvalidComponentError: bad component(expected host component): localhost:3000
/home/henrikas/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/uri/generic.rb:395:in `check_host'
/home/henrikas/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/uri/generic.rb:409:in `host='
/home/henrikas/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/uri/generic.rb:180:in `initialize'
/home/henrikas/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/uri/generic.rb:126:in `new'
/home/henrikas/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/uri/generic.rb:126:in `build'
/home/henrikas/.rvm/gems/ruby-1.9.2-p290@web/gems/roadie-1.1.3/lib/roadie/inliner.rb:186:in `absolute_url_base'

The spec:

it "should render content properly" do
    email = UserMailer.mass_mail(user.email, subject, body).deliver
    email.body.should =~/#{body}/
end

The mailer:

  layout 'user_mailer/layout'
  def mass_mail(to, subject, body)
    @content = body
    @user = User.find_by_email(to)
    mail(
        :to => to,
        :subject => subject
    ) do |format|
      format.html
    end
  end

I am using ruby 1.9.2p290

My gemfile.lock:

GEM
  remote: http://rubygems.org/
  specs:
    abstract (1.0.0)
    actionmailer (3.0.10)
      actionpack (= 3.0.10)
      mail (~> 2.2.19)
    actionpack (3.0.10)
      activemodel (= 3.0.10)
      activesupport (= 3.0.10)
      builder (~> 2.1.2)
      erubis (~> 2.6.6)
      i18n (~> 0.5.0)
      rack (~> 1.2.1)
      rack-mount (~> 0.6.14)
      rack-test (~> 0.5.7)
      tzinfo (~> 0.3.23)
    activemerchant (1.17.0)
      activesupport (>= 2.3.11)
      braintree (>= 2.0.0)
      builder (>= 2.0.0)
      json (>= 1.5.1)
    activemodel (3.0.10)
      activesupport (= 3.0.10)
      builder (~> 2.1.2)
      i18n (~> 0.5.0)
    activerecord (3.0.10)
      activemodel (= 3.0.10)
      activesupport (= 3.0.10)
      arel (~> 2.0.10)
      tzinfo (~> 0.3.23)
    activeresource (3.0.10)
      activemodel (= 3.0.10)
      activesupport (= 3.0.10)
    activesupport (3.0.10)
    acts_as_list (0.1.4)
    addressable (2.2.6)
    arel (2.0.10)
    braintree (2.11.0)
      builder (>= 2.0.0)
    builder (2.1.2)
    cancan (1.6.5)
    chunky_png (1.2.1)
    cocaine (0.2.0)
    coderay (0.9.8)
    compass (0.11.5)
      chunky_png (~> 1.2)
      fssm (>= 0.2.7)
      sass (~> 3.1)
    css_parser (1.2.6)
      addressable
      rdoc
    daemons (1.1.4)
    delayed_job (2.1.4)
      activesupport (~> 3.0)
      daemons
    diff-lcs (1.1.3)
    erubis (2.6.6)
      abstract (>= 1.0.0)
    factory_girl (2.0.5)
    formtastic (1.2.4)
      actionpack (>= 2.3.7)
      activesupport (>= 2.3.7)
      i18n (~> 0.4)
    fssm (0.2.7)
    growl (1.0.3)
    guard (0.6.2)
      thor (~> 0.14.6)
    guard-rspec (0.4.3)
      guard (>= 0.4.0)
    guard-spork (0.2.1)
      guard (>= 0.2.2)
      spork (>= 0.8.4)
    haml (3.1.2)
    hirb (0.5.0)
    html5-boilerplate (1.0.0)
      compass (>= 0.11.1)
    i18n (0.5.0)
    jquery-rails (1.0.19)
      railties (~> 3.0)
      thor (~> 0.14)
    json (1.5.3)
    mail (2.2.19)
      activesupport (>= 2.3.6)
      i18n (>= 0.4.0)
      mime-types (~> 1.16)
      treetop (~> 1.4.8)
    method_source (0.6.5)
      ruby_parser (>= 2.0.5)
    mime-types (1.16)
    mysql2 (0.2.11)
    nokogiri (1.5.0)
    paperclip (2.3.16)
      activerecord (>= 2.3.0)
      activesupport (>= 2.3.2)
      cocaine (>= 0.0.2)
      mime-types
    polyglot (0.3.2)
    pry (0.9.5)
      coderay (>= 0.9.8)
      method_source (>= 0.6.5)
      ruby_parser (>= 2.0.5)
      slop (~> 2.1.0)
    rack (1.2.3)
    rack-mount (0.6.14)
      rack (>= 1.0.0)
    rack-test (0.5.7)
      rack (>= 1.0)
    rails (3.0.10)
      actionmailer (= 3.0.10)
      actionpack (= 3.0.10)
      activerecord (= 3.0.10)
      activeresource (= 3.0.10)
      activesupport (= 3.0.10)
      bundler (~> 1.0)
      railties (= 3.0.10)
    rails3-generators (0.17.4)
      railties (>= 3.0.0)
    rails3-jquery-autocomplete (1.0.5)
      rails (~> 3.0)
    railties (3.0.10)
      actionpack (= 3.0.10)
      activesupport (= 3.0.10)
      rake (>= 0.8.7)
      rdoc (~> 3.4)
      thor (~> 0.14.4)
    rake (0.8.7)
    rb-fsevent (0.4.3.1)
    rdoc (3.9.4)
    remarkable (4.0.0.alpha4)
      rspec (>= 2.0.0.alpha11)
    remarkable_activemodel (4.0.0.alpha4)
      remarkable (~> 4.0.0.alpha4)
      rspec (>= 2.0.0.alpha11)
    remarkable_activerecord (4.0.0.alpha4)
      remarkable (~> 4.0.0.alpha4)
      remarkable_activemodel (~> 4.0.0.alpha4)
      rspec (>= 2.0.0.alpha11)
    roadie (1.1.3)
      actionmailer (~> 3.0)
      css_parser
      nokogiri (>= 1.4.4)
    rspec (2.6.0)
      rspec-core (~> 2.6.0)
      rspec-expectations (~> 2.6.0)
      rspec-mocks (~> 2.6.0)
    rspec-core (2.6.4)
    rspec-expectations (2.6.0)
      diff-lcs (~> 1.1.2)
    rspec-mocks (2.6.0)
    rspec-rails (2.6.1)
      actionpack (~> 3.0)
      activesupport (~> 3.0)
      railties (~> 3.0)
      rspec (~> 2.6.0)
    ruby_parser (2.3.0)
      sexp_processor (~> 3.0)
    sass (3.1.7)
    sexp_processor (3.0.6)
    slop (2.1.0)
    spork (0.8.5)
    thor (0.14.6)
    treetop (1.4.10)
      polyglot
      polyglot (>= 0.3.1)
    tzinfo (0.3.29)
    will_paginate (3.0.0)

PLATFORMS
  ruby

DEPENDENCIES
  activemerchant
  acts_as_list
  authlogic!
  cancan
  compass
  delayed_job (= 2.1.4)
  exception_notification!
  factory_girl
  flag_shih_tzu!
  formtastic
  growl
  guard
  guard-rspec
  guard-spork
  haml
  hirb
  html5-boilerplate
  i18n
  jquery-rails
  json
  mysql2 (~> 0.2.0)
  paperclip
  pry
  rails (>= 3.0.0)
  rails3-generators
  rails3-jquery-autocomplete
  rake (= 0.8.7)
  rb-fsevent
  remarkable (>= 4.0.0.alpha2)
  remarkable_activemodel (>= 4.0.0.alpha2)
  remarkable_activerecord (>= 4.0.0.alpha2)
  roadie
  rspec (~> 2.5)
  rspec-rails (~> 2.5)
  spork
  webtopay!
  will_paginate (~> 3.0.pre2)

URI::InvalidURIError

Hello,

My development.rb settings:
config.action_mailer.default_url_options = { :host => 'localhost', port: '3000' }

When I try to deliver some email, I'll get this error:

URI::InvalidURIError
bad URI(is not URI?): 

Full trace:

/usr/local/rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/uri/common.rb:176:in `split'
/usr/local/rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/uri/common.rb:211:in `parse'
/usr/local/rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/uri/common.rb:747:in `parse'
roadie (2.3.2) lib/roadie/inliner.rb:199:in `block in all_link_elements_with_url'
nokogiri (1.5.5) lib/nokogiri/xml/node_set.rb:239:in `block in each'
nokogiri (1.5.5) lib/nokogiri/xml/node_set.rb:238:in `upto'
nokogiri (1.5.5) lib/nokogiri/xml/node_set.rb:238:in `each'
roadie (2.3.2) lib/roadie/inliner.rb:199:in `map'
roadie (2.3.2) lib/roadie/inliner.rb:199:in `all_link_elements_with_url'
roadie (2.3.2) lib/roadie/inliner.rb:203:in `all_link_elements_to_be_inlined_with_url'
roadie (2.3.2) lib/roadie/inliner.rb:101:in `extract_link_elements'
roadie (2.3.2) lib/roadie/inliner.rb:50:in `block in execute'
roadie (2.3.2) lib/roadie/inliner.rb:75:in `block in adjust_html'
roadie (2.3.2) lib/roadie/inliner.rb:74:in `tap'
roadie (2.3.2) lib/roadie/inliner.rb:74:in `adjust_html'
roadie (2.3.2) lib/roadie/inliner.rb:47:in `execute'
roadie (2.3.2) lib/roadie.rb:6:in `inline_css'
roadie (2.3.2) lib/roadie/action_mailer_extensions.rb:42:in `inline_style_response'
roadie (2.3.2) lib/roadie/action_mailer_extensions.rb:32:in `block in collect_responses_and_parts_order_with_inline_styles'
roadie (2.3.2) lib/roadie/action_mailer_extensions.rb:32:in `map'
roadie (2.3.2) lib/roadie/action_mailer_extensions.rb:32:in `collect_responses_and_parts_order_with_inline_styles'
actionmailer (3.2.7) lib/action_mailer/base.rb:636:in `mail'
roadie (2.3.2) lib/roadie/action_mailer_extensions.rb:25:in `mail_with_inline_styles'
/Users/palodelincak/Web/imango_shop/app/mailers/order_mailer.rb:13:in `recieved'
actionpack (3.2.7) lib/abstract_controller/base.rb:167:in `process_action'
actionpack (3.2.7) lib/abstract_controller/base.rb:121:in `process'
actionpack (3.2.7) lib/abstract_controller/rendering.rb:45:in `process'
actionmailer (3.2.7) lib/action_mailer/base.rb:457:in `process'
actionmailer (3.2.7) lib/action_mailer/base.rb:452:in `initialize'
actionmailer (3.2.7) lib/action_mailer/base.rb:439:in `new'
actionmailer (3.2.7) lib/action_mailer/base.rb:439:in `method_missing'
/Users/palodelincak/Web/imango_shop/app/mailers/order_mailer.rb:20:in `recieved'
/usr/local/rvm/gems/ruby-1.9.3-p194@imango/bundler/gems/mail_view-86a56c55143b/lib/mail_view.rb:38:in `call'
/usr/local/rvm/gems/ruby-1.9.3-p194@imango/bundler/gems/mail_view-86a56c55143b/lib/mail_view.rb:19:in `call'
journey (1.0.4) lib/journey/router.rb:68:in `block in call'
journey (1.0.4) lib/journey/router.rb:56:in `each'
journey (1.0.4) lib/journey/router.rb:56:in `call'
actionpack (3.2.7) lib/action_dispatch/routing/route_set.rb:600:in `call'
mongoid (2.4.12) lib/rack/mongoid/middleware/identity_map.rb:33:in `block in call'
mongoid (2.4.12) lib/mongoid.rb:133:in `unit_of_work'
mongoid (2.4.12) lib/rack/mongoid/middleware/identity_map.rb:33:in `call'
sass (3.2.1) lib/sass/plugin/rack.rb:54:in `call'
rack-pjax (0.5.9) lib/rack/pjax.rb:12:in `call'
warden (1.2.1) lib/warden/manager.rb:35:in `block in call'
warden (1.2.1) lib/warden/manager.rb:34:in `catch'
warden (1.2.1) lib/warden/manager.rb:34:in `call'
actionpack (3.2.7) lib/action_dispatch/middleware/best_standards_support.rb:17:in `call'
rack (1.4.1) lib/rack/etag.rb:23:in `call'
rack (1.4.1) lib/rack/conditionalget.rb:25:in `call'
actionpack (3.2.7) lib/action_dispatch/middleware/head.rb:14:in `call'
actionpack (3.2.7) lib/action_dispatch/middleware/params_parser.rb:21:in `call'
actionpack (3.2.7) lib/action_dispatch/middleware/flash.rb:242:in `call'
rack (1.4.1) lib/rack/session/abstract/id.rb:205:in `context'
rack (1.4.1) lib/rack/session/abstract/id.rb:200:in `call'
actionpack (3.2.7) lib/action_dispatch/middleware/cookies.rb:338:in `call'
dragonfly (0.9.12) lib/dragonfly/cookie_monster.rb:9:in `call'
actionpack (3.2.7) lib/action_dispatch/middleware/callbacks.rb:28:in `block in call'
activesupport (3.2.7) lib/active_support/callbacks.rb:405:in `_run__3431187308779137096__call__1682571658200229250__callbacks'
activesupport (3.2.7) lib/active_support/callbacks.rb:405:in `__run_callback'
activesupport (3.2.7) lib/active_support/callbacks.rb:385:in `_run_call_callbacks'
activesupport (3.2.7) lib/active_support/callbacks.rb:81:in `run_callbacks'
actionpack (3.2.7) lib/action_dispatch/middleware/callbacks.rb:27:in `call'
actionpack (3.2.7) lib/action_dispatch/middleware/reloader.rb:65:in `call'
actionpack (3.2.7) lib/action_dispatch/middleware/remote_ip.rb:31:in `call'
actionpack (3.2.7) lib/action_dispatch/middleware/debug_exceptions.rb:16:in `call'
actionpack (3.2.7) lib/action_dispatch/middleware/show_exceptions.rb:56:in `call'
railties (3.2.7) lib/rails/rack/logger.rb:26:in `call_app'
railties (3.2.7) lib/rails/rack/logger.rb:16:in `call'
actionpack (3.2.7) lib/action_dispatch/middleware/request_id.rb:22:in `call'
rack (1.4.1) lib/rack/methodoverride.rb:21:in `call'
rack (1.4.1) lib/rack/runtime.rb:17:in `call'
activesupport (3.2.7) lib/active_support/cache/strategy/local_cache.rb:72:in `call'
rack (1.4.1) lib/rack/lock.rb:15:in `call'
rack-rewrite (1.2.1) lib/rack/rewrite.rb:20:in `call'
actionpack (3.2.7) lib/action_dispatch/middleware/static.rb:62:in `call'
dragonfly (0.9.12) lib/dragonfly/middleware.rb:13:in `call'
rack-cache (1.2) lib/rack/cache/context.rb:136:in `forward'
rack-cache (1.2) lib/rack/cache/context.rb:245:in `fetch'
rack-cache (1.2) lib/rack/cache/context.rb:185:in `lookup'
rack-cache (1.2) lib/rack/cache/context.rb:66:in `call!'
rack-cache (1.2) lib/rack/cache/context.rb:51:in `call'
airbrake (3.1.2) lib/airbrake/rack.rb:42:in `call'
airbrake (3.1.2) lib/airbrake/user_informer.rb:12:in `call'
railties (3.2.7) lib/rails/engine.rb:479:in `call'
railties (3.2.7) lib/rails/application.rb:220:in `call'
rack (1.4.1) lib/rack/content_length.rb:14:in `call'
railties (3.2.7) lib/rails/rack/log_tailer.rb:17:in `call'
rack (1.4.1) lib/rack/handler/webrick.rb:59:in `service'
/usr/local/rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/webrick/httpserver.rb:138:in `service'
/usr/local/rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/webrick/httpserver.rb:94:in `run'
/usr/local/rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/webrick/server.rb:191:in `block in start_thread'

Problem with Delayed Job

Hi, rodie works properly when being used alone, but causes delayed job to crush when both of them are enabled. I think this is happening because works override the mail method, but I don't know if this happens only to me or is a know problem.

Any thoughts? Hints?

Thanks in advance.
Lucas

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.