title: Rails idiom to avoid duplicates in has_many :through
body: <p>I have a standard many-to-many relationship between users and roles in my Rails app:</p>
  
  <pre><code>class User &lt; ActiveRecord::Base
    has_many :user_roles
    has_many :roles, :through =&gt; :user_roles
  end
  </code></pre>
  
  <p>I want to make sure that a user can only be assigned any role once.  Any attempt to insert a duplicate should ignore the request, not throw an error or cause validation failure.  What I really want to represent is a "set", where inserting an element that already exists in the set has no effect.  {1,2,3} U {1} = {1,2,3}, not {1,1,2,3}.</p>
  
  <p>I realize that I can do it like this:</p>
  
  <pre><code>user.roles &lt;&lt; role unless user.roles.include?(role)
  </code></pre>
  
  <p>or by creating a wrapper method (e.g. <code>add_to_roles(role)</code>), but I was hoping for some idiomatic way to make it automatic via the association, so that I can write:</p>
  
  <pre><code>user.roles &lt;&lt; role  # automatically checks roles.include?
  </code></pre>
  
  <p>and it just does the work for me.  This way, I don't have to remember to check for dups or to use the custom method.  Is there something in the framework I'm missing?  I first thought the :uniq option to has_many would do it, but it's basically just "select distinct."</p>
  
  <p>Is there a way to do this declaratively?  If not, maybe by using an association extension?</p>
  
  <p>Here's an example of how the default behavior fails:</p>
  
  <pre>    &gt;&gt; <strong>u = User.create</strong>
        User Create (0.6ms)   INSERT INTO "users" ("name") VALUES(NULL)
      =&gt; #&lt;User id: 3, name: nil&gt;
      &gt;&gt; <strong>u.roles &lt;&lt; Role.first</strong>
        Role Load (0.5ms)   SELECT * FROM "roles" LIMIT 1
        UserRole Create (0.5ms)   INSERT INTO "user_roles" ("role_id", "user_id") VALUES(1, 3)
        Role Load (0.4ms)   SELECT "roles".* FROM "roles" INNER JOIN "user_roles" ON "roles".id = "user_roles".role_id WHERE (("user_roles".user_id = 3)) 
      =&gt; [#&lt;Role id: 1, name: "1"&gt;]
      &gt;&gt; <strong>u.roles &lt;&lt; Role.first</strong>
        Role Load (0.4ms)   SELECT * FROM "roles" LIMIT 1
        UserRole Create (0.5ms)   INSERT INTO "user_roles" ("role_id", "user_id") VALUES(1, 3)
      <strong><em>=&gt; [#&lt;Role id: 1, name: "1"&gt;, #&lt;Role id: 1, name: "1"&gt;]</em></strong></pre>