defender 2.0.2 → 2.0.3

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.
@@ -1,66 +1,57 @@
1
- ##
2
- # Main Defender module. Everything should be under this module.
1
+ # Public: The main Defender module. This is the namespace under which all of
2
+ # Defender is.
3
3
  module Defender
4
4
  autoload :VERSION, 'defender/version'
5
5
  autoload :Version, 'defender/version'
6
6
  autoload :Spammable, 'defender/spammable'
7
7
  autoload :DefenderError, 'defender/defender_error'
8
-
9
- ##
10
- # Set this to your Defensio API key. Get one at http://defensio.com.
8
+
9
+ # Public: Set the Defensio API key. Get one at http://defensio.com.
11
10
  #
12
- # @see Defender::Spammable::ClassMethods.configure_defender
11
+ # Defender will not work without a Defensio API key.
13
12
  #
14
- # @param [String] api_key The Defensio API key.
13
+ # Returns the Defensio API key string.
15
14
  def self.api_key=(api_key)
16
15
  @api_key = api_key.to_s
17
16
  end
18
-
19
- ##
20
- # Returns the Defensio API key set with {Defender.api_key=}.
21
- #
22
- # @see Defender.api_key=
23
- # @return [String] The API key if set.
24
- # @return [nil] If no API key has been set.
17
+
18
+ # Public: Returns the Defensio API key String set with #api_key=
25
19
  def self.api_key
26
20
  @api_key
27
21
  end
28
22
 
29
- ##
30
- # You most probably don't need to set this. It is used to replace the backend
31
- # when running the tests. If you for any reason need to use another backend
32
- # than the defensio gem, set this. The object needs to respond to the same
33
- # methods as the Defensio object does.
23
+ # Public: Returns whether Defender is in "test mode".
34
24
  #
35
- # @param [Defensio] defensio The Defensio backend
25
+ # When in test mode, you can specify what kind of response you want in the
26
+ # content field. If you want a comment to be marked as spam with a
27
+ # spaminess of 0.85, you write [spam,0.85] somewhere in the content field
28
+ # of the document. If you want a malicious response with a spaminess of
29
+ # 0.99 you write [malicious,0.99], and for an innocent response you write
30
+ # [innocent,0.25]. This is the preferred way of testing, and if you test
31
+ # by writing "spammy" comments, you might hurt the Defensio performance.
32
+ def self.test_mode
33
+ !!@test_mode
34
+ end
35
+
36
+ # Public: Enables/disables Defender's test mode. You can use this, or the
37
+ # configure_defender method to enable the test mode, but you should
38
+ # probably use this if you only temporarily want to enable the test mode.
39
+ def self.test_mode=(test_mode)
40
+ @test_mode = test_mode
41
+ end
42
+
43
+ # Internal: This is for replacing the Defensio backend when running tests.
36
44
  def self.defensio=(defensio)
37
45
  @defensio = defensio
38
46
  end
39
47
 
40
- ##
41
- # The Defensio backend. If no backend has been set yet, this will create one
42
- # with the api key set with {Defender.api_key}.
48
+ # Internal: The Defensio backend. If no backend has been set yet, this will
49
+ # create one with the API key set with #api_key=.
43
50
  #
44
- # @return [Defensio] The Defensio backend
51
+ # Returns the Defensio backend.
45
52
  def self.defensio
46
53
  return @defensio if defined?(@defensio)
47
54
  require 'defensio'
48
55
  @defensio ||= Defensio.new(@api_key, "Defender | #{VERSION} | Henrik Hodne | dvyjones@binaryhex.com")
49
56
  end
50
-
51
- ##
52
- # Calls a defensio method and wraps in error handling.
53
- #
54
- # Returns false if the method failed, otherwise returns whatever the method returns
55
- #
56
- # @param [Symbol] method Which method to call.
57
- # @return [false, Array(Fixnum, Hash)]
58
- def self.call(method, *args)
59
- code, data = defensio.send(method, *args)
60
- if code == 200 && data['status'] == 'success'
61
- [code, data]
62
- else
63
- false
64
- end
65
- end
66
57
  end
@@ -1,5 +1,4 @@
1
1
  module Defender
2
- ##
3
2
  # Defender exception class. This will be thrown on errors returned by
4
3
  # Defensio.
5
4
  class DefenderError < StandardError
@@ -1,96 +1,94 @@
1
1
  module Defender
2
- ##
3
- # Include this in your ActiveModel-supporting model to enable spam
4
- # filtering.
2
+ # Includes all the model magic for Defender. This module should be included in
3
+ # your model to enable Defender on it. Defender does some automatic detection
4
+ # on your setup, but you need to do some configuration yourself. You can set
5
+ # the API key in two different ways. If you only use Defender on one model,
6
+ # you can configure the API key in the model with the configure_defender
7
+ # method. If you prefer to use initializers, or you have multiple models, you
8
+ # probably want to define it in an initializer. You can do this by calling
9
+ # Defender.api_key directly, like this:
5
10
  #
6
- # Defender will try to autodetect details about your rails setup, but you
7
- # need to do some configuration yourself. If you already have an application
8
- # config file that loads into a constant named APP_CONFIG, and your
9
- # comment model has an attribute named 'body', 'content' or 'comment'
10
- # including the comment body, then you are almost ready to go. Create the
11
- # 'spam' and 'defensio_sig' attribute in the database (a boolean and a
12
- # string, respectively) and then include {Defender::Spammable} in your
13
- # model. You can now call #spam? on your model after saving it.
14
- # Congratulations!
11
+ # Defender.api_key = 'This is your API key'
15
12
  #
16
- # Defender requires the model to have callbacks, more exactly, the
17
- # before_save callback. Most ActiveModel-libraries should have that, so you
18
- # should only need to worry if you're making your own models. Just look at
19
- # {Defender::Test::Comment} for an example comment model.
13
+ # Defender only requires your models to have a before_save callback, but since
14
+ # most ActiveModel libraries should have this you only need to worry about it
15
+ # if you're making your own models. Just have a look at
16
+ # Defender::Test::Comment for an example comment model.
20
17
  module Spammable
21
- # These are the default attribute names Defender will pull information
22
- # from if no other names are configured. So the content of the comment
23
- # will be pulled from 'body', if that attribute exists. Otherwise, it will
24
- # pull from 'content'. If that doesn't exist either, it will pull from
25
- # 'comment'. If that doesn't exist either, you should configure your own
26
- # name in {Defender::Spammable::ClassMethods.configure_defender}.
18
+ # Public: These are the default attribute names Defender will pull
19
+ # information from if no other names are configured. So the content of the
20
+ # comment will be pulled from 'body', if that attribute exists. Otherwise,
21
+ # it will be pulled from 'content'. If that doesn't exist either, it will
22
+ # pull from 'comment'. If that attribute doesn't exist either, you should
23
+ # configure your own attributes with the configure_defender method.
27
24
  DEFENSIO_KEYS = {
28
25
  'content' => [:body, :content, :comment],
29
26
  'author-name' => [:author_name, :author],
30
27
  'author-email' => [:author_email, :email],
31
28
  'author-ip' => [:author_ip, :ip],
32
29
  'author-url' => [:author_url, :url]
33
- }
34
-
35
- ##
36
- # These methods will be pulled in as class methods in your model when
37
- # including {Defender::Spammable}.
30
+ }.freeze
31
+
32
+ # Public: Methods that will be included as class methods when including
33
+ # Defender::Spammable into your model.
38
34
  module ClassMethods
39
- ##
40
- # Configure Defender by passing a set of options.
41
- #
42
- # @param [Hash] options Options for configuring Defender.
43
- # @option options [Hash] :keys Mapping between field names in the
44
- # database and in defensio.
45
- # @option options [String] :api_key Your Defensio API key. Get one at
46
- # defensio.com.
47
- # @option options [Boolean] :test_mode Set Defender in test mode. See
48
- # {#test_mode}.
49
- #
35
+ # Public: Configures various Defender options.
36
+ #
37
+ # options - The hash options used to configure Defender:
38
+ # :keys - A Hash which maps field names in the database to
39
+ # Defensio field names (optional).
40
+ # :api_key - Your Defensio API key String (optional).
41
+ # :test_mode - Set this to true to enable the test mode. See
42
+ # Defender.test_mode for more information.
43
+ #
44
+ # Examples
45
+ #
46
+ # configure_defender :keys => { 'content' => :comment_content },
47
+ # :api_key => 'Your API key.', :test_mode => true
48
+ #
49
+ # Returns nothing
50
50
  def configure_defender(options)
51
51
  keys = options.delete(:keys)
52
52
  _defensio_keys.merge!(keys) unless keys.nil?
53
53
  api_key = options.delete(:api_key)
54
54
  Defender.api_key = api_key unless api_key.nil?
55
- self.test_mode = options.delete(:test_mode)
55
+ Defender.test_mode = options.delete(:test_mode)
56
56
  end
57
-
58
- ##
59
- # Set this to true to put Defender in "test mode". When in test mode, you
60
- # can check if your code is working properly you can specify in the
61
- # content field what kind of response you want. If you want a comment to
62
- # be marked as spam with a spaminess of 0.85 you write [spam,0.85]
63
- # somewhere in the content field of the document. If you want a malicious
64
- # response with a spaminess of 0.99 you write [malicious,0.99] and for an
65
- # innocent response you write [innocent,0.25]. This is the preferred way
66
- # of testing, if you write spammy comments you might hurt the Defensio
67
- # performance.
68
- attr_accessor :test_mode
69
-
70
- ##
71
- # Returns the key-attribute mapping used.
72
- #
73
- # Will automatically set it to the defaults in {DEFENSIO_KEYS} if
74
- # nothing else has been set before.
57
+
58
+ # Deprecated: Returns whether Defender is in "test mode".
59
+ #
60
+ # Use Defender.test_mode instead.
61
+ def test_mode
62
+ Defender.test_mode
63
+ end
64
+
65
+ # Deprecated: Enables/disables Defender's test mode.
66
+ #
67
+ # Use Defender.test_mode= instead.
68
+ def test_mode=(test_mode)
69
+ Defender.test_mode = test_mode
70
+ end
71
+
72
+ # Internal: Returns the key-attribute mapping Hash used.
73
+ #
74
+ # This will default to DEFENSIO_KEYS, but can be modified.
75
+ #
76
+ # The Public API has access to this through configure_defender.
75
77
  def _defensio_keys
76
78
  @_defensio_keys ||= DEFENSIO_KEYS.dup
77
79
  end
78
80
  end
79
-
80
- ##
81
- # These methods will be pulled in as instance methods in your model when
82
- # including {Defender::Spammable}.
81
+
82
+ # Public: Methods that will be included as instance methods when including
83
+ # Defender::Spammable into your model.
83
84
  module InstanceMethods
84
- ##
85
- # Returns true if the comment is recognized as spam or malicious.
86
- #
87
- # If the value is stored in the database that value will be returned.
88
- # If nil is returned, the comment has not yet been submitted to
89
- # Defensio.
85
+ # Public: Whether the comment is recognized a malicious comment or as
86
+ # spam.
90
87
  #
91
- # @raise [Defender::DefenderError] Raised if there is no spam attribute
92
- # in the model.
93
- # @return [Boolean] Whether the comment is spam or not.
88
+ # Returns the Boolean value stored in the database, or nil if the comment
89
+ # hasn't been submitted to Defensio yet.
90
+ # Raises Defender::DefenderError if there is no spam attribute in the
91
+ # model.
94
92
  def spam?
95
93
  if self.new_record?
96
94
  nil
@@ -100,36 +98,80 @@ module Defender
100
98
  raise Defender::DefenderError, 'You need to add a spam attribute to the model'
101
99
  end
102
100
  end
103
-
104
- ##
105
- # Pass in some data to be sent to defensio. You can use this method to
106
- # pass in more data that you don't want to save in the model.
101
+
102
+ # Public: Report a false positive to Defensio and update the spam
103
+ # attribute.
104
+ #
105
+ # A false positive is a legitimate comment incorrectly marked as spam.
106
+ #
107
+ # This must be done within 30 days of the comment originally being
108
+ # submitted. If you need to update this after that, just set the spam
109
+ # attribute on your model and save it.
107
110
  #
108
- # This can be called several times if you want to add more data or
109
- # update data already added (using the same key twice will overwrite).
111
+ # Raises a Defender::DefenderError if Defensio returns an error.
112
+ def false_positive!
113
+ document = Defender.defensio.put_document(self.defensio_sig, {'allow' => 'true'}).last
114
+ if document['status'] == 'failed'
115
+ raise DefenderError, document['message']
116
+ end
117
+ update_attribute(:spam, false)
118
+ end
119
+
120
+ # Public: Report a false negative to Defensio and update the spam
121
+ # attribute.
110
122
  #
111
- # Returns the data to be sent. Pass without a parameter to not modify
112
- # the data.
123
+ # A false negative is a spammy comment incorrectly marked as legitimate.
113
124
  #
114
- # @param [Hash<String => Object>] data The data to send to defensio. See
115
- # the README for the possible key values.
125
+ # This must be done within 30 days of the comment originally being
126
+ # submitted. If you need to update this after that, just set the spam
127
+ # attribute on your model and save it.
128
+ #
129
+ # Raises a Defender::DefenderError if Defensio returns an error.
130
+ def false_negative!
131
+ document = Defender.defensio.put_document(self.defensio_sig, {'allow' => 'false'}).last
132
+ if document['status'] == 'failed'
133
+ raise DefenderError, document['message']
134
+ end
135
+ update_attribute(:spam, true)
136
+ end
137
+
138
+ # Public: Pass in more data to be sent to Defensio. You should use this
139
+ # for data you don't want to save in the model, for instance HTTP headers.
140
+ #
141
+ # This can be called several times, the new data will be merged into the
142
+ # existing data. If you use the same key twice, the new value will
143
+ # overwrite the old.
144
+ #
145
+ # data - The Hash data to send to Defensio. Check the README for the
146
+ # possible keys.
147
+ #
148
+ # Examples
149
+ #
150
+ # def create # A Rails controller action
151
+ # @comment = Comment.new(params[:comment])
152
+ # @comment.defensio_data(
153
+ # 'http-headers' => request.env.map {|k,v| "#{k}: #{v}" }.join("\n")
154
+ # )
155
+ # end
156
+ #
157
+ # Returns the data to be sent.
116
158
  def defensio_data(data={})
117
159
  @_defensio_data ||= {}
118
160
  @_defensio_data.merge!(data)
119
161
  @_defensio_data
120
162
  end
121
-
163
+
122
164
  private
123
-
124
- ##
125
- # The callback that will be run before a document is saved.
165
+
166
+ # Internal: The callback that will be run before a document is created..
126
167
  #
127
- # This will gather all the data and send it off to Defensio, and then
128
- # set the spam and defensio_sig attributes (and spaminess if it's
129
- # defined) before the model will be saved.
168
+ # This will gather all the data and send it off to Defensio, and then set
169
+ # the spam and defensio_sig attributes (and spaminess if it's defined)
170
+ # before the model will be saved.
130
171
  #
131
- # @raise Defender::DefenderError If Defensio returns an error.
132
- def _defender_before_save
172
+ # Raises a Defender::DefenderError if Defensio returns an error. Please
173
+ # note that this will cancel the save.
174
+ def _defender_before_create
133
175
  data = {}
134
176
  _defensio_keys.each do |key, names|
135
177
  next if names.nil?
@@ -137,61 +179,60 @@ module Defender
137
179
  end
138
180
  data.merge!({
139
181
  'platform' => 'ruby',
140
- 'type' => (self.class.test_mode ? 'test' : 'comment')
182
+ 'type' => (Defender.test_mode ? 'test' : 'comment')
141
183
  })
142
184
  data.merge!(defensio_data) if defined?(@_defensio_data)
143
- document = Defender.defensio.post_document(data).last
144
- if document['status'] == 'failed'
145
- raise DefenderError, document['message']
185
+ if document = Defender.defensio.post_document(data).last
186
+ if document['status'] == 'failed'
187
+ raise DefenderError, document['message']
188
+ end
189
+ self.spam = !document['allow']
190
+ self.defensio_sig = document['signature'].to_s
191
+ self.spaminess = document['spaminess'] if self.respond_to?(:spaminess=)
192
+ else
193
+ raise DefenderError, 'Got nil response from Defensio API, service might be down'
146
194
  end
147
- self.spam = !document['allow']
148
- self.defensio_sig = document['signature'].to_s
149
- self.spaminess = document['spaminess'] if self.respond_to?(:spaminess=)
195
+ true
150
196
  end
151
-
152
- ##
153
- # Return the first attribute value from a list of attribute names/
197
+
198
+ # Internal: Returns value of the first attribute that exists in a list of
199
+ # attributes.
154
200
  #
155
- # @param [Array<Symbol>, Symbol] names A list of attribute names
156
- # @return [] The attribute value of the first existing attribute
157
- # @return [nil] If no attribute was found (or if attribute value is nil)
201
+ # names - A Symbol or Array of Symbols representing the attribute name(s).
158
202
  def _pick_attribute(names)
159
203
  [names].flatten.each do |name|
160
204
  return self.send(name) if self.respond_to?(name)
161
205
  end
162
206
  return nil
163
207
  end
164
-
165
- ##
166
- # Retrieves the Defensio document from the server if it hasn't been
167
- # retrieved before or if the first parameter is true.
168
- #
169
- # @param [Boolean] force Pass true to force a refetch, otherwise it will
170
- # get the cached document (if one is cached).
171
- # @return [Hash] The document retrieved from the server.
208
+
209
+ # Internal: Retrieves the Defensio document from the server if it hasn't
210
+ # been retrieved before or if the first parameter is true.
211
+ #
212
+ # force - A Boolean representing whether to force a refetch. If a refetch
213
+ # isn't forced, the document will only be fetched if it hasn't
214
+ # been fetched already.
215
+ #
216
+ # Returns the Hash with the information retrieved from the server.
172
217
  def _get_defensio_document(force=false)
173
- if force || @_defensio_document.nil?
218
+ if force || !defined?(@_defensio_document) || @_defensio_document.nil?
174
219
  @_defensio_document = Defender.defensio.get_document(self.defensio_sig).last
175
220
  end
176
221
  @_defensio_document
177
222
  end
178
-
179
- ##
180
- # Wrapper for {Defender::Spammable::ClassMethods._defensio_keys}.
181
- #
182
- # @see Defender::Spammable::ClassMethods._defensio_keys
223
+
224
+ # Internal: Wrapper for the class method with the same name.
183
225
  def _defensio_keys
184
226
  self.class._defensio_keys
185
227
  end
186
228
  end
187
-
188
- ##
189
- # Includes {Defender::Spammable::ClassMethods} and
190
- # {Defender::Spammable::InstanceMethods} and sets up save callback.
229
+
230
+ # Internal: Includes the ClassMethods and InstanceMethods and sets up the
231
+ # before_save callback.
191
232
  def self.included(receiver)
192
233
  receiver.extend ClassMethods
193
234
  receiver.send :include, InstanceMethods
194
- receiver.send :before_save, :_defender_before_save
235
+ receiver.send :before_create, :_defender_before_create
195
236
  end
196
237
  end
197
- end
238
+ end
@@ -1,36 +1,48 @@
1
1
  require 'active_model'
2
2
 
3
- ##
4
3
  # Stuff to test Defender. You probably shouldn't use this in your application,
5
4
  # but it is included as an example of the minimum needed for a valid setup.
6
5
  module Defender::Test
7
- ##
8
6
  # A fake Comment class to use. No need to require ActiveRecord and set up an
9
7
  # actual database. We will use ActiveModel for callbacks though.
10
8
  class Comment
11
9
  extend ActiveModel::Naming
12
10
  extend ActiveModel::Callbacks
13
11
  define_model_callbacks :save
14
-
12
+ define_model_callbacks :create
13
+
15
14
  # We now have a "valid" model, let's bring in Defender.
16
15
  include Defender::Spammable
17
-
16
+
18
17
  attr_accessor :body, :author, :author_ip, :created_at, :spam, :defensio_sig
19
-
20
- ##
18
+
21
19
  # Returns true if save has been called, false otherwise.
22
20
  def new_record?
23
- !@saved ||= false
21
+ !(@saved ||= false)
24
22
  end
25
-
26
- ##
23
+
27
24
  # Run save callback and make {Defender::Test::Comment.new_record?} return false.
28
- def save
29
- _run_save_callbacks do
30
- # We're not actually saving anything, just letting Defender know we
31
- # would be.
25
+ #
26
+ # The with_callbacks method is only for using this as a test interface.
27
+ def save(with_callbacks=true)
28
+ if with_callbacks
29
+ _run_save_callbacks do
30
+ # We're not actually saving anything, just letting Defender know we
31
+ # would be.
32
+ unless defined?(@saved) && @saved
33
+ _run_create_callbacks do
34
+ @saved = true
35
+ end
36
+ end
37
+ end
38
+ else
32
39
  @saved = true
33
40
  end
34
41
  end
42
+
43
+ def update_attribute(name, value)
44
+ self.send("#{name}=".to_sym, value)
45
+ self.save
46
+ end
35
47
  end
36
- end
48
+ end
@@ -1,4 +1,4 @@
1
1
  module Defender
2
2
  # Current Defender version
3
- VERSION = '2.0.2'
3
+ VERSION = '2.0.3'
4
4
  end
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: 2.0.2
4
+ version: 2.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,12 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-09-02 00:00:00.000000000 +02:00
13
- default_executable:
12
+ date: 2012-03-26 00:00:00.000000000 Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: defensio
17
- requirement: &2153287360 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
18
17
  none: false
19
18
  requirements:
20
19
  - - ~>
@@ -22,10 +21,15 @@ dependencies:
22
21
  version: 0.9.1
23
22
  type: :runtime
24
23
  prerelease: false
25
- version_requirements: *2153287360
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.9.1
26
30
  - !ruby/object:Gem::Dependency
27
31
  name: activemodel
28
- requirement: &2153286860 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
29
33
  none: false
30
34
  requirements:
31
35
  - - ! '>='
@@ -33,10 +37,15 @@ dependencies:
33
37
  version: 3.0.0
34
38
  type: :runtime
35
39
  prerelease: false
36
- version_requirements: *2153286860
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 3.0.0
37
46
  - !ruby/object:Gem::Dependency
38
47
  name: bundler
39
- requirement: &2153286480 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
40
49
  none: false
41
50
  requirements:
42
51
  - - ! '>='
@@ -44,10 +53,15 @@ dependencies:
44
53
  version: '0'
45
54
  type: :development
46
55
  prerelease: false
47
- version_requirements: *2153286480
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
48
62
  - !ruby/object:Gem::Dependency
49
63
  name: rake
50
- requirement: &2153285640 !ruby/object:Gem::Requirement
64
+ requirement: !ruby/object:Gem::Requirement
51
65
  none: false
52
66
  requirements:
53
67
  - - ! '>='
@@ -55,7 +69,12 @@ dependencies:
55
69
  version: '0'
56
70
  type: :development
57
71
  prerelease: false
58
- version_requirements: *2153285640
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
59
78
  description:
60
79
  email:
61
80
  - dvyjones@dvyjones.com
@@ -68,7 +87,6 @@ files:
68
87
  - lib/defender/test/comment.rb
69
88
  - lib/defender/version.rb
70
89
  - lib/defender.rb
71
- has_rdoc: true
72
90
  homepage:
73
91
  licenses: []
74
92
  post_install_message:
@@ -83,7 +101,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
83
101
  version: '0'
84
102
  segments:
85
103
  - 0
86
- hash: -2741349701634154017
104
+ hash: -4330272566146055507
87
105
  required_rubygems_version: !ruby/object:Gem::Requirement
88
106
  none: false
89
107
  requirements:
@@ -92,10 +110,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
92
110
  version: '0'
93
111
  segments:
94
112
  - 0
95
- hash: -2741349701634154017
113
+ hash: -4330272566146055507
96
114
  requirements: []
97
115
  rubyforge_project:
98
- rubygems_version: 1.6.2
116
+ rubygems_version: 1.8.21
99
117
  signing_key:
100
118
  specification_version: 3
101
119
  summary: ActiveModel plugin for Defensio.