defender 2.0.2 → 2.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.