defender 2.0.2 → 2.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/defender.rb +31 -40
- data/lib/defender/defender_error.rb +0 -1
- data/lib/defender/spammable.rb +163 -122
- data/lib/defender/test/comment.rb +26 -14
- data/lib/defender/version.rb +1 -1
- metadata +33 -15
data/lib/defender.rb
CHANGED
@@ -1,66 +1,57 @@
|
|
1
|
-
|
2
|
-
#
|
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
|
-
#
|
11
|
+
# Defender will not work without a Defensio API key.
|
13
12
|
#
|
14
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
data/lib/defender/spammable.rb
CHANGED
@@ -1,96 +1,94 @@
|
|
1
1
|
module Defender
|
2
|
-
|
3
|
-
#
|
4
|
-
#
|
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
|
-
#
|
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
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
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
|
22
|
-
# from if no other names are configured. So the content of the
|
23
|
-
# will be pulled from 'body', if that attribute exists. Otherwise,
|
24
|
-
#
|
25
|
-
# 'comment'. If that doesn't exist either, you should
|
26
|
-
#
|
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
|
-
#
|
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
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
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
|
-
|
55
|
+
Defender.test_mode = options.delete(:test_mode)
|
56
56
|
end
|
57
|
-
|
58
|
-
|
59
|
-
#
|
60
|
-
#
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
#
|
73
|
-
#
|
74
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
92
|
-
#
|
93
|
-
#
|
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
|
-
#
|
106
|
-
#
|
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
|
-
#
|
109
|
-
|
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
|
-
#
|
112
|
-
# the data.
|
123
|
+
# A false negative is a spammy comment incorrectly marked as legitimate.
|
113
124
|
#
|
114
|
-
#
|
115
|
-
#
|
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
|
-
#
|
129
|
-
#
|
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
|
-
#
|
132
|
-
|
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' => (
|
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
|
-
|
145
|
-
|
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
|
-
|
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
|
-
#
|
197
|
+
|
198
|
+
# Internal: Returns value of the first attribute that exists in a list of
|
199
|
+
# attributes.
|
154
200
|
#
|
155
|
-
#
|
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
|
-
#
|
167
|
-
#
|
168
|
-
#
|
169
|
-
#
|
170
|
-
#
|
171
|
-
#
|
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
|
-
#
|
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 :
|
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
|
-
|
21
|
+
!(@saved ||= false)
|
24
22
|
end
|
25
|
-
|
26
|
-
##
|
23
|
+
|
27
24
|
# Run save callback and make {Defender::Test::Comment.new_record?} return false.
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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
|
data/lib/defender/version.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: 2.0.
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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: -
|
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: -
|
113
|
+
hash: -4330272566146055507
|
96
114
|
requirements: []
|
97
115
|
rubyforge_project:
|
98
|
-
rubygems_version: 1.
|
116
|
+
rubygems_version: 1.8.21
|
99
117
|
signing_key:
|
100
118
|
specification_version: 3
|
101
119
|
summary: ActiveModel plugin for Defensio.
|