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.
- 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.
|