defender 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +0 -6
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/lib/defender.rb +40 -69
- data/spec/defender_spec.rb +64 -19
- data/spec/spec_helper.rb +1 -0
- metadata +12 -2
    
        data/README.rdoc
    CHANGED
    
    | @@ -2,12 +2,6 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            This is a Ruby wrapper of the Defensio[http://defensio.com] spam filtering API. To use this library, you need an API key from Defensio. Go ahead and {get one}[http://defensio.com/signup/].
         | 
| 4 4 |  | 
| 5 | 
            -
            == Testing
         | 
| 6 | 
            -
             | 
| 7 | 
            -
            To run the tests, you need to pass the api key and owner url with the environment keys API_KEY and API_OWNER_URL, like this:
         | 
| 8 | 
            -
              rake spec API_KEY="key1234" API_OWNER_URL="http://myblog.com"
         | 
| 9 | 
            -
            The tests will fail with invalid keys and urls.
         | 
| 10 | 
            -
             | 
| 11 5 | 
             
            == Note on Patches/Pull Requests
         | 
| 12 6 |  | 
| 13 7 | 
             
            * Fork the project.
         | 
    
        data/Rakefile
    CHANGED
    
    | @@ -12,6 +12,7 @@ begin | |
| 12 12 | 
             
                gem.authors = ["Henrik Hodne"]
         | 
| 13 13 | 
             
                gem.add_development_dependency "rspec", ">= 1.2.9"
         | 
| 14 14 | 
             
                gem.add_development_dependency "yard", ">= 0"
         | 
| 15 | 
            +
                gem.add_development_dependency "mocha", ">= 0.9.8"
         | 
| 15 16 | 
             
                # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
         | 
| 16 17 | 
             
              end
         | 
| 17 18 | 
             
              Jeweler::GemcutterTasks.new
         | 
    
        data/VERSION
    CHANGED
    
    | @@ -1 +1 @@ | |
| 1 | 
            -
            0.1. | 
| 1 | 
            +
            0.1.1
         | 
    
        data/lib/defender.rb
    CHANGED
    
    | @@ -5,10 +5,12 @@ class Defender | |
| 5 5 | 
             
              # The Defensio API version currently supported by Defender
         | 
| 6 6 | 
             
              API_VERSION = "1.2"
         | 
| 7 7 |  | 
| 8 | 
            +
              ROOT_URL = "http://api.defensio.com/"
         | 
| 9 | 
            +
              
         | 
| 8 10 | 
             
              DEFAULT_OPTIONS = {
         | 
| 9 | 
            -
                service_type | 
| 10 | 
            -
                api_key | 
| 11 | 
            -
                owner_url | 
| 11 | 
            +
                :service_type => "blog",
         | 
| 12 | 
            +
                :api_key => "",
         | 
| 13 | 
            +
                :owner_url => ""
         | 
| 12 14 | 
             
              }
         | 
| 13 15 |  | 
| 14 16 | 
             
              ##
         | 
| @@ -48,13 +50,9 @@ class Defender | |
| 48 50 | 
             
                # otherwise.
         | 
| 49 51 | 
             
                #
         | 
| 50 52 | 
             
                # @return [Boolean]
         | 
| 51 | 
            -
                def spam | 
| 52 | 
            -
                  @spam
         | 
| 53 | 
            -
                end
         | 
| 53 | 
            +
                def spam?; @spam; end
         | 
| 54 54 |  | 
| 55 | 
            -
                def to_s
         | 
| 56 | 
            -
                  @signature
         | 
| 57 | 
            -
                end
         | 
| 55 | 
            +
                def to_s; @signature; end
         | 
| 58 56 | 
             
              end
         | 
| 59 57 |  | 
| 60 58 | 
             
              ##
         | 
| @@ -66,47 +64,39 @@ class Defender | |
| 66 64 | 
             
                # Defensio on this blog.
         | 
| 67 65 | 
             
                #
         | 
| 68 66 | 
             
                # @return [Float<0..1>]
         | 
| 69 | 
            -
                 | 
| 67 | 
            +
                def accuracy; @response["accuracy"]; end
         | 
| 70 68 |  | 
| 71 69 | 
             
                ##
         | 
| 72 70 | 
             
                # The number of spam comments caught by the filter.
         | 
| 73 | 
            -
                 | 
| 71 | 
            +
                def spam; @response["spam"]; end
         | 
| 74 72 |  | 
| 75 73 | 
             
                ##
         | 
| 76 74 | 
             
                # The number of ham (legitimate) comments accepted by the filter.
         | 
| 77 | 
            -
                 | 
| 75 | 
            +
                def ham; @response["ham"]; end
         | 
| 78 76 |  | 
| 79 77 | 
             
                ##
         | 
| 80 78 | 
             
                # The number of times a legitimate message was retrained from the spambox
         | 
| 81 79 | 
             
                # (i.e. "de-spammed" by the user)
         | 
| 82 | 
            -
                 | 
| 80 | 
            +
                def false_positives; @response["false-positives"]; end
         | 
| 83 81 |  | 
| 84 82 | 
             
                ##
         | 
| 85 83 | 
             
                # The number of times a spam message was retrained from comments box (i.e.
         | 
| 86 84 | 
             
                # "de-legitimized" by the user)
         | 
| 87 | 
            -
                 | 
| 85 | 
            +
                def false_negatives; @response["false-negatives"]; end
         | 
| 88 86 |  | 
| 89 87 | 
             
                ##
         | 
| 90 88 | 
             
                # A boolean value indicating whether Defensio is still in its initial
         | 
| 91 89 | 
             
                # learning phase.
         | 
| 92 90 | 
             
                #
         | 
| 93 91 | 
             
                # @return [Boolean]
         | 
| 94 | 
            -
                 | 
| 92 | 
            +
                def learning; @response["learning"]; end
         | 
| 95 93 |  | 
| 96 94 | 
             
                ##
         | 
| 97 95 | 
             
                # More details on the reason(s) why Defensio is still in its initial
         | 
| 98 96 | 
             
                # learning phase.
         | 
| 99 | 
            -
                 | 
| 97 | 
            +
                def learning_status; @response["learning-status"]; end
         | 
| 100 98 |  | 
| 101 | 
            -
                def initialize(response)
         | 
| 102 | 
            -
                  @accuracy = response["accuracy"]
         | 
| 103 | 
            -
                  @spam = response["spam"]
         | 
| 104 | 
            -
                  @ham = response["ham"]
         | 
| 105 | 
            -
                  @false_positives = response["false-positives"]
         | 
| 106 | 
            -
                  @false_negatives = response["false-negatives"]
         | 
| 107 | 
            -
                  @learning = response["learning"]
         | 
| 108 | 
            -
                  @learning_status = response["learning-status"]
         | 
| 109 | 
            -
                end
         | 
| 99 | 
            +
                def initialize(response); @response = response; end
         | 
| 110 100 | 
             
              end
         | 
| 111 101 |  | 
| 112 102 | 
             
              attr_accessor :service_type, :api_key, :owner_url
         | 
| @@ -131,11 +121,9 @@ class Defender | |
| 131 121 | 
             
              # @return [Hash]
         | 
| 132 122 | 
             
              def self.options_to_parameters(options)
         | 
| 133 123 | 
             
                opts = {}
         | 
| 134 | 
            -
                options.each do |key,  | 
| 135 | 
            -
                   | 
| 136 | 
            -
                     | 
| 137 | 
            -
                  end
         | 
| 138 | 
            -
                  opts[key.to_s.gsub("_", "-").downcase] = value.to_s
         | 
| 124 | 
            +
                options.each do |key, val|
         | 
| 125 | 
            +
                  opts[key.to_s.gsub("_", "-").downcase] = val.respond_to?(:strftime) ? 
         | 
| 126 | 
            +
                    val.strftime("%Y/%m/%d") : val.to_s
         | 
| 139 127 | 
             
                end
         | 
| 140 128 | 
             
                opts
         | 
| 141 129 | 
             
              end
         | 
| @@ -162,40 +150,27 @@ class Defender | |
| 162 150 | 
             
              # @return [Boolean]
         | 
| 163 151 | 
             
              # @see http://defensio.com/api/#validate-key
         | 
| 164 152 | 
             
              def valid_key?
         | 
| 165 | 
            -
                 | 
| 166 | 
            -
                  response = call_action("validate-key")
         | 
| 167 | 
            -
                  if response["status"] == "success"
         | 
| 168 | 
            -
                    return true
         | 
| 169 | 
            -
                  else
         | 
| 170 | 
            -
                    return false
         | 
| 171 | 
            -
                  end
         | 
| 172 | 
            -
                rescue StandardError
         | 
| 173 | 
            -
                  return false
         | 
| 174 | 
            -
                end
         | 
| 153 | 
            +
                call_action("validate-key")["status"] == "success" ? true : false
         | 
| 175 154 | 
             
              end
         | 
| 176 155 |  | 
| 177 156 | 
             
              ##
         | 
| 178 157 | 
             
              # Announce an article existence. This should (if feasible) be called when an
         | 
| 179 158 | 
             
              # article or blogpost is created so Defensio can analyse it.
         | 
| 180 159 | 
             
              #
         | 
| 181 | 
            -
              # @param [ | 
| 182 | 
            -
              # @ | 
| 183 | 
            -
              # @ | 
| 160 | 
            +
              # @param [Hash] opts All options are required.
         | 
| 161 | 
            +
              # @option opts [#to_s] :article_title The title of the article
         | 
| 162 | 
            +
              # @option opts [#to_s] :article_author The name of the author of the article
         | 
| 163 | 
            +
              # @option opts [#to_s] :article_author_email The email address of the person posting the
         | 
| 184 164 | 
             
              #   article.
         | 
| 185 | 
            -
              # @ | 
| 186 | 
            -
              # @ | 
| 165 | 
            +
              # @option opts [#to_s] :article_content The content of the article itself.
         | 
| 166 | 
            +
              # @option opts [#to_s] :permalink The permalink of the article just posted.
         | 
| 187 167 | 
             
              # @raise [StandardError] If the call fails, a StandardError is raised with
         | 
| 188 168 | 
             
              #   the error message given from Defensio.
         | 
| 189 169 | 
             
              # @return [Boolean] Returns true if the article was successfully announced,
         | 
| 190 170 | 
             
              #   raises StandardError otherwise.
         | 
| 191 171 | 
             
              # @see http://defensio.com/api/#announce-article
         | 
| 192 | 
            -
              def announce_article( | 
| 193 | 
            -
                response = call_action( | 
| 194 | 
            -
                                       "article-title" => title.to_s,
         | 
| 195 | 
            -
                                       "article-author" => author.to_s,
         | 
| 196 | 
            -
                                       "article-author-email" => author_email.to_s,
         | 
| 197 | 
            -
                                       "article-content" => content,
         | 
| 198 | 
            -
                                       "permalink" => permalink)
         | 
| 172 | 
            +
              def announce_article(opts={})
         | 
| 173 | 
            +
                response = call_action(Defender.options_to_parameters(opts))
         | 
| 199 174 | 
             
                true
         | 
| 200 175 | 
             
              end
         | 
| 201 176 |  | 
| @@ -258,9 +233,7 @@ class Defender | |
| 258 233 | 
             
              # @return [Boolean] Returns true if the comments were successfully marked,
         | 
| 259 234 | 
             
              #   raises StandardError otherwise.
         | 
| 260 235 | 
             
              def report_false_negatives(signatures)
         | 
| 261 | 
            -
                 | 
| 262 | 
            -
                                       "signatures" => signatures.map(&:to_s).join(","))
         | 
| 263 | 
            -
                true
         | 
| 236 | 
            +
                report_false(:negatives, signatures)
         | 
| 264 237 | 
             
              end
         | 
| 265 238 |  | 
| 266 239 | 
             
              ##
         | 
| @@ -277,9 +250,7 @@ class Defender | |
| 277 250 | 
             
              # @return [Boolean] Returns true if the comments were successfully marked,
         | 
| 278 251 | 
             
              #   raises StandardError otherwise.
         | 
| 279 252 | 
             
              def report_false_positives(signatures)
         | 
| 280 | 
            -
                 | 
| 281 | 
            -
                                       "signatures" => signatures.map(&:to_s).join(","))
         | 
| 282 | 
            -
                true
         | 
| 253 | 
            +
                report_false(:positives, signatures)
         | 
| 283 254 | 
             
              end
         | 
| 284 255 |  | 
| 285 256 | 
             
              ##
         | 
| @@ -293,6 +264,12 @@ class Defender | |
| 293 264 | 
             
              end
         | 
| 294 265 |  | 
| 295 266 | 
             
              private
         | 
| 267 | 
            +
                def report_false(type, signatures)
         | 
| 268 | 
            +
                  call_action("report-false-#{type}",
         | 
| 269 | 
            +
                                       "signatures" => signatures.join(","))
         | 
| 270 | 
            +
                  true
         | 
| 271 | 
            +
                end
         | 
| 272 | 
            +
              
         | 
| 296 273 | 
             
                ##
         | 
| 297 274 | 
             
                # Returns the url for the given action.
         | 
| 298 275 | 
             
                #
         | 
| @@ -301,11 +278,7 @@ class Defender | |
| 301 278 | 
             
                # @raise [APIKeyError] Raises this if no API key is given.
         | 
| 302 279 | 
             
                def url(action)
         | 
| 303 280 | 
             
                  raise APIKeyError unless @api_key.length > 0
         | 
| 304 | 
            -
                  " | 
| 305 | 
            -
                  "#{@service_type}/" \
         | 
| 306 | 
            -
                  "#{Defender::API_VERSION}/" \
         | 
| 307 | 
            -
                  "#{action}/" \
         | 
| 308 | 
            -
                  "#{@api_key}.yaml"
         | 
| 281 | 
            +
                  "#{ROOT_URL}#{@service_type}/#{API_VERSION}/#{action}/#{@api_key}.yaml"
         | 
| 309 282 | 
             
                end
         | 
| 310 283 |  | 
| 311 284 | 
             
                ##
         | 
| @@ -317,12 +290,10 @@ class Defender | |
| 317 290 | 
             
                # @raise [APIKeyError] If an invalid (or no) API key is given, this is
         | 
| 318 291 | 
             
                #   raised
         | 
| 319 292 | 
             
                def call_action(action, params={})
         | 
| 320 | 
            -
                   | 
| 321 | 
            -
             | 
| 322 | 
            -
                   | 
| 323 | 
            -
                    raise  | 
| 324 | 
            -
                  else
         | 
| 293 | 
            +
                  response = Net::HTTP.post_form(URI.parse(url(action)),
         | 
| 294 | 
            +
                    {"owner-url" => @owner_url}.merge(params))
         | 
| 295 | 
            +
                  response.code == 401 ?
         | 
| 296 | 
            +
                    raise(APIKeyError) :
         | 
| 325 297 | 
             
                    Defender.raise_if_error(YAML.load(response.body)["defensio-result"])
         | 
| 326 | 
            -
                  end
         | 
| 327 298 | 
             
                end
         | 
| 328 299 | 
             
            end
         | 
    
        data/spec/defender_spec.rb
    CHANGED
    
    | @@ -1,6 +1,10 @@ | |
| 1 1 | 
             
            require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
         | 
| 2 2 |  | 
| 3 3 | 
             
            describe "Defender" do
         | 
| 4 | 
            +
              before(:each) do
         | 
| 5 | 
            +
                @defender = Defender.new(:api_key => "validkey", :owner_url => "validurl")
         | 
| 6 | 
            +
              end
         | 
| 7 | 
            +
             | 
| 4 8 | 
             
              it "should raise a StandardError if a method fails" do
         | 
| 5 9 | 
             
                lambda do
         | 
| 6 10 | 
             
                  Defender.raise_if_error({
         | 
| @@ -11,26 +15,39 @@ describe "Defender" do | |
| 11 15 | 
             
              end
         | 
| 12 16 |  | 
| 13 17 | 
             
              it "should return the correct URL for any given action" do
         | 
| 14 | 
            -
                 | 
| 15 | 
            -
                d.instance_eval do
         | 
| 18 | 
            +
                @defender.instance_eval do
         | 
| 16 19 | 
             
                  url("foobar")
         | 
| 17 | 
            -
                end.should == "http://api.defensio.com/blog/#{Defender::API_VERSION}/foobar/ | 
| 20 | 
            +
                end.should == "http://api.defensio.com/blog/#{Defender::API_VERSION}/foobar/validkey.yaml"
         | 
| 18 21 | 
             
              end
         | 
| 19 22 |  | 
| 20 23 | 
             
              it "should correctly identify a valid API key" do
         | 
| 21 | 
            -
                 | 
| 22 | 
            -
             | 
| 24 | 
            +
                @defender.stubs(:call_action).with("validate-key").returns(
         | 
| 25 | 
            +
                  {"status" => "success", "message" => ""}
         | 
| 26 | 
            +
                )
         | 
| 27 | 
            +
                @defender.valid_key?.should be_true
         | 
| 23 28 | 
             
              end
         | 
| 24 29 |  | 
| 25 30 | 
             
              it "should correctly identify an invalid API key" do
         | 
| 26 | 
            -
                 | 
| 27 | 
            -
             | 
| 31 | 
            +
                @defender.stubs(:call_action).with("validate-key").returns(
         | 
| 32 | 
            +
                  {"status" => "fail", "message" => "Invalid key"}
         | 
| 33 | 
            +
                )
         | 
| 34 | 
            +
                @defender.valid_key?.should be_false
         | 
| 28 35 | 
             
              end
         | 
| 29 36 |  | 
| 30 37 | 
             
              it "should correctly identify a spammy comment" do
         | 
| 31 | 
            -
                 | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 38 | 
            +
                @defender.
         | 
| 39 | 
            +
                  stubs(:call_action).
         | 
| 40 | 
            +
                  with('audit-comment', {
         | 
| 41 | 
            +
                    "user-ip" => "127.0.0.1",
         | 
| 42 | 
            +
                    "article-date" => Time.now.strftime("%Y/%m/%d"),
         | 
| 43 | 
            +
                    "comment-author" => "Henrik Hodne",
         | 
| 44 | 
            +
                    "comment-type" => "comment",
         | 
| 45 | 
            +
                    "test-force" => "spam,0.5000",
         | 
| 46 | 
            +
                  }).
         | 
| 47 | 
            +
                  returns(
         | 
| 48 | 
            +
                  {"signature" => "abc123", "spam" => true, "spaminess" => 0.5}
         | 
| 49 | 
            +
                )
         | 
| 50 | 
            +
                @defender.audit_comment(
         | 
| 34 51 | 
             
                  :user_ip => "127.0.0.1",
         | 
| 35 52 | 
             
                  :article_date => Time.now,
         | 
| 36 53 | 
             
                  :comment_author => "Henrik Hodne",
         | 
| @@ -40,9 +57,19 @@ describe "Defender" do | |
| 40 57 | 
             
              end
         | 
| 41 58 |  | 
| 42 59 | 
             
              it "should correctly identify a meaty comment" do
         | 
| 43 | 
            -
                 | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 60 | 
            +
                @defender.
         | 
| 61 | 
            +
                  stubs(:call_action).
         | 
| 62 | 
            +
                  with('audit-comment', {
         | 
| 63 | 
            +
                    "user-ip" => "127.0.0.1",
         | 
| 64 | 
            +
                    "article-date" => Time.now.strftime("%Y/%m/%d"),
         | 
| 65 | 
            +
                    "comment-author" => "Henrik Hodne",
         | 
| 66 | 
            +
                    "comment-type" => "comment",
         | 
| 67 | 
            +
                    "test-force" => "ham,0.1000",
         | 
| 68 | 
            +
                  }).
         | 
| 69 | 
            +
                  returns(
         | 
| 70 | 
            +
                  {"signature" => "abc123", "spam" => false, "spaminess" => 0.1}
         | 
| 71 | 
            +
                )
         | 
| 72 | 
            +
                @defender.audit_comment(
         | 
| 46 73 | 
             
                  :user_ip => "127.0.0.1",
         | 
| 47 74 | 
             
                  :article_date => Time.now,
         | 
| 48 75 | 
             
                  :comment_author => "Henrik Hodne",
         | 
| @@ -52,9 +79,19 @@ describe "Defender" do | |
| 52 79 | 
             
              end
         | 
| 53 80 |  | 
| 54 81 | 
             
              it "should correctly set the spaminess" do
         | 
| 55 | 
            -
                 | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 82 | 
            +
                @defender.
         | 
| 83 | 
            +
                  stubs(:call_action).
         | 
| 84 | 
            +
                  with('audit-comment', {
         | 
| 85 | 
            +
                    "user-ip" => "127.0.0.1",
         | 
| 86 | 
            +
                    "article-date" => Time.now.strftime("%Y/%m/%d"),
         | 
| 87 | 
            +
                    "comment-author" => "Henrik Hodne",
         | 
| 88 | 
            +
                    "comment-type" => "comment",
         | 
| 89 | 
            +
                    "test-force" => "spam,0.5000",
         | 
| 90 | 
            +
                  }).
         | 
| 91 | 
            +
                  returns(
         | 
| 92 | 
            +
                  {"signature" => "abc123", "spam" => true, "spaminess" => 0.5}
         | 
| 93 | 
            +
                )
         | 
| 94 | 
            +
                @defender.audit_comment(
         | 
| 58 95 | 
             
                  :user_ip => "127.0.0.1",
         | 
| 59 96 | 
             
                  :article_date => Time.now,
         | 
| 60 97 | 
             
                  :comment_author => "Henrik Hodne",
         | 
| @@ -64,8 +101,16 @@ describe "Defender" do | |
| 64 101 | 
             
              end
         | 
| 65 102 |  | 
| 66 103 | 
             
              it "should fail without valid API credentials" do
         | 
| 67 | 
            -
                 | 
| 68 | 
            -
             | 
| 104 | 
            +
                @defender.
         | 
| 105 | 
            +
                  stubs(:call_action).
         | 
| 106 | 
            +
                  with('audit-comment', {
         | 
| 107 | 
            +
                    "user-ip" => "127.0.0.1",
         | 
| 108 | 
            +
                    "article-date" => Time.now.strftime("%Y/%m/%d"),
         | 
| 109 | 
            +
                    "comment-author" => "Henrik Hodne",
         | 
| 110 | 
            +
                    "comment-type" => "comment",
         | 
| 111 | 
            +
                    "test-force" => "ham,0.1000",
         | 
| 112 | 
            +
                  }).
         | 
| 113 | 
            +
                  raises(StandardError)
         | 
| 69 114 | 
             
                lambda {
         | 
| 70 115 | 
             
                  d.audit_comment(
         | 
| 71 116 | 
             
                    :user_ip => "127.0.0.1",
         | 
| @@ -74,6 +119,6 @@ describe "Defender" do | |
| 74 119 | 
             
                    :comment_type => "comment",
         | 
| 75 120 | 
             
                    :test_force => "ham,0.1000"
         | 
| 76 121 | 
             
                  )
         | 
| 77 | 
            -
                }.should raise_error(StandardError | 
| 122 | 
            +
                }.should raise_error(StandardError)
         | 
| 78 123 | 
             
              end
         | 
| 79 124 | 
             
            end
         | 
    
        data/spec/spec_helper.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification 
         | 
| 2 2 | 
             
            name: defender
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version 
         | 
| 4 | 
            -
              version: 0.1. | 
| 4 | 
            +
              version: 0.1.1
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors: 
         | 
| 7 7 | 
             
            - Henrik Hodne
         | 
| @@ -9,7 +9,7 @@ autorequire: | |
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 11 |  | 
| 12 | 
            -
            date: 2009-11- | 
| 12 | 
            +
            date: 2009-11-09 00:00:00 +01:00
         | 
| 13 13 | 
             
            default_executable: 
         | 
| 14 14 | 
             
            dependencies: 
         | 
| 15 15 | 
             
            - !ruby/object:Gem::Dependency 
         | 
| @@ -32,6 +32,16 @@ dependencies: | |
| 32 32 | 
             
                  - !ruby/object:Gem::Version 
         | 
| 33 33 | 
             
                    version: "0"
         | 
| 34 34 | 
             
                version: 
         | 
| 35 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 36 | 
            +
              name: mocha
         | 
| 37 | 
            +
              type: :development
         | 
| 38 | 
            +
              version_requirement: 
         | 
| 39 | 
            +
              version_requirements: !ruby/object:Gem::Requirement 
         | 
| 40 | 
            +
                requirements: 
         | 
| 41 | 
            +
                - - ">="
         | 
| 42 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 43 | 
            +
                    version: 0.9.8
         | 
| 44 | 
            +
                version: 
         | 
| 35 45 | 
             
            description: A wrapper of the Defensio spam filtering service.
         | 
| 36 46 | 
             
            email: henrik.hodne@binaryhex.com
         | 
| 37 47 | 
             
            executables: []
         |