acts_as_textcaptcha 4.3.0 → 4.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.simplecov +9 -0
- data/.travis.yml +13 -4
- data/Appraisals +1 -1
- data/CHANGELOG.md +68 -34
- data/CODE_OF_CONDUCT.md +54 -30
- data/CONTRIBUTING.md +14 -9
- data/Gemfile +0 -2
- data/LICENSE +165 -0
- data/README.md +194 -223
- data/Rakefile +21 -13
- data/acts_as_textcaptcha.gemspec +52 -32
- data/bin/console +2 -5
- data/bin/setup +7 -0
- data/gemfiles/rails_3.gemfile +1 -1
- data/gemfiles/rails_4.gemfile +1 -1
- data/gemfiles/rails_5.gemfile +2 -2
- data/lib/acts_as_textcaptcha.rb +3 -1
- data/lib/acts_as_textcaptcha/errors.rb +19 -0
- data/lib/acts_as_textcaptcha/textcaptcha.rb +91 -84
- data/lib/acts_as_textcaptcha/textcaptcha_api.rb +39 -44
- data/lib/acts_as_textcaptcha/textcaptcha_cache.rb +12 -15
- data/{config/textcaptcha.yml → lib/acts_as_textcaptcha/textcaptcha_config.rb} +16 -4
- data/lib/acts_as_textcaptcha/textcaptcha_helper.rb +14 -14
- data/lib/acts_as_textcaptcha/version.rb +1 -1
- data/lib/tasks/textcaptcha.rake +9 -14
- metadata +64 -31
- data/.coveralls.yml +0 -1
- data/LICENSE.txt +0 -21
- data/test/schema.rb +0 -34
- data/test/test_helper.rb +0 -28
- data/test/test_models.rb +0 -69
- data/test/textcaptcha_api_test.rb +0 -46
- data/test/textcaptcha_cache_test.rb +0 -25
- data/test/textcaptcha_helper_test.rb +0 -68
- data/test/textcaptcha_test.rb +0 -198
data/README.md
CHANGED
@@ -1,287 +1,258 @@
|
|
1
1
|
## ActAsTextcaptcha
|
2
2
|
|
3
|
-
[](http://rubygems.org/gems/acts_as_textcaptcha)
|
4
|
+
[](https://travis-ci.org/matthutchinson/acts_as_textcaptcha)
|
5
|
+
[](https://depfu.com/github/matthutchinson/acts_as_textcaptcha)
|
6
|
+
[](https://codeclimate.com/github/matthutchinson/acts_as_textcaptcha/maintainability)
|
7
|
+
[](https://codeclimate.com/github/matthutchinson/acts_as_textcaptcha/test_coverage)
|
8
|
+
|
9
|
+
ActsAsTextcaptcha provides spam protection for Rails models with a text-based
|
10
|
+
logic question captcha. Questions are fetched from [Rob
|
11
|
+
Tuley's](https://twitter.com/robtuley)
|
12
|
+
[textcaptcha.com](http://textcaptcha.com/). They can be solved easily by humans
|
13
|
+
but are tough for robots to crack.
|
14
|
+
|
15
|
+
The gem can also be configured with your own questions; as an alternative, or as
|
16
|
+
a fallback to handle any API issues. For reasons on why logic based captchas
|
17
|
+
are a good idea visit [textcaptcha.com](http://textcaptcha.com).
|
18
|
+
|
19
|
+
## Requirements
|
20
|
+
|
21
|
+
* [Ruby](http://ruby-lang.org/) >= 2.1.0
|
22
|
+
* [Rails](http://github.com/rails/rails) >= 3
|
23
|
+
* [Rails.cache](http://guides.rubyonrails.org/caching_with_rails.html#cache-stores)
|
24
24
|
|
25
25
|
## Demo
|
26
26
|
|
27
27
|
Try a [working demo here](https://acts-as-textcaptcha-demo.herokuapp.com)!
|
28
|
-
|
29
|
-
|
30
|
-
configured with acts_as_textcaptcha). See
|
31
|
-
[here](https://github.com/matthutchinson/acts_as_textcaptcha_demo) for more
|
32
|
-
details.
|
28
|
+
**Or** one-click deploy your own demo app at Heroku. See
|
29
|
+
[here](https://github.com/matthutchinson/acts_as_textcaptcha_demo) for details.
|
33
30
|
|
34
31
|
[](https://www.heroku.com/deploy?template=https://github.com/matthutchinson/acts_as_textcaptcha_demo/tree/master)
|
35
32
|
|
36
|
-
##
|
33
|
+
## Installation
|
37
34
|
|
38
|
-
|
35
|
+
Add this line to your Gemfile and run `bundle install`:
|
39
36
|
|
40
|
-
|
37
|
+
```ruby
|
38
|
+
gem 'acts_as_textcaptcha'
|
39
|
+
```
|
41
40
|
|
42
|
-
Add
|
41
|
+
Add this to models you'd like to protect:
|
43
42
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
43
|
+
```ruby
|
44
|
+
class Comment < ApplicationRecord
|
45
|
+
acts_as_textcaptcha api_key: 'TEXTCAPTCHA_API_IDENTITY'
|
46
|
+
# see below for more config options
|
47
|
+
end
|
48
|
+
```
|
48
49
|
|
49
|
-
|
50
|
-
|
51
|
-
can be contacted
|
50
|
+
[Rob](https://twitter.com/robtuley) requests that your
|
51
|
+
`TEXTCAPTCHA_API_IDENTITY` be some reference to yourself (e.g. an email address,
|
52
|
+
domain or similar) so you can be contacted should any usage problem occur.
|
52
53
|
|
53
|
-
|
54
|
-
for the record, like so;
|
54
|
+
In your controller's `new` action call the `textcaptcha` method:
|
55
55
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
56
|
+
```ruby
|
57
|
+
def new
|
58
|
+
@comment = Comment.new
|
59
|
+
@comment.textcaptcha
|
60
|
+
end
|
61
|
+
```
|
60
62
|
|
61
|
-
Finally
|
62
|
-
|
63
|
-
block as you like;
|
63
|
+
Finally add the question and answer fields to your form using the
|
64
|
+
`textcaptcha_fields` helper. Arrange the HTML within this block as you like.
|
64
65
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
66
|
+
```ruby
|
67
|
+
<%= textcaptcha_fields(f) do %>
|
68
|
+
<div class="field">
|
69
|
+
<%= f.label :textcaptcha_answer, @comment.textcaptcha_question %><br/>
|
70
|
+
<%= f.text_field :textcaptcha_answer, :value => '' %>
|
71
|
+
</div>
|
72
|
+
<% end %>
|
73
|
+
```
|
71
74
|
|
72
|
-
|
73
|
-
|
75
|
+
If you'd prefer to construct your own form elements, take a look at the HTML
|
76
|
+
produced
|
74
77
|
[here](https://github.com/matthutchinson/acts_as_textcaptcha/blob/master/lib/acts_as_textcaptcha/textcaptcha_helper.rb).
|
75
78
|
|
76
|
-
|
77
|
-
configuration](http://guides.rubyonrails.org/caching_with_rails.html#cache-stores)
|
78
|
-
changed with Rails 5 and this gem **requires** a working Rails.cache store to
|
79
|
-
exist.
|
79
|
+
## Configuration
|
80
80
|
|
81
|
-
|
82
|
-
having problems please refer to the 3.0 [upgrade
|
83
|
-
guide](https://github.com/matthutchinson/acts_as_textcaptcha/wiki/Upgrading-from-3.0.10).
|
81
|
+
The following options are available (only `api_key` is required):
|
84
82
|
|
85
|
-
|
83
|
+
* **api_key** (_required_) - a reference to yourself (e.g. your email or domain).
|
84
|
+
* **questions** (_optional_) - array of your own questions and answers (see below).
|
85
|
+
* **cache_expiry_minutes** (_optional_) - how long valid answers are cached for (default 10 minutes).
|
86
|
+
* **raise_errors** (_optional_) - if true, API or network errors are raised (default false, errors logged).
|
87
|
+
* **api_endpoint** (_optional_) - set your own JSON API endpoint to fetch from (see below).
|
86
88
|
|
87
|
-
|
88
|
-
`perform_textcaptcha?` method. If it returns false, no questions will be fetched
|
89
|
-
from the web service and captcha validation is disabled.
|
89
|
+
For example:
|
90
90
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
91
|
+
```ruby
|
92
|
+
class Comment < ApplicationRecord
|
93
|
+
acts_as_textcaptcha api_key: 'TEXTCAPTCHA_API_IDENTITY_KEY',
|
94
|
+
raise_errors: false,
|
95
|
+
cache_expiry_minutes: 10,
|
96
|
+
questions: [
|
97
|
+
{ 'question' => '1+1', 'answers' => '2,two' },
|
98
|
+
{ 'question' => 'The green hat is what color?', 'answers' => 'green' }
|
99
|
+
]
|
100
|
+
end
|
101
|
+
```
|
95
102
|
|
96
|
-
|
97
|
-
updating). Here is a typical example showing how to overwrite the
|
98
|
-
`perform_textcaptcha?` method, while maintaining the new record check.
|
103
|
+
### YAML config
|
99
104
|
|
100
|
-
|
101
|
-
|
105
|
+
You can apply an app wide config with a `config/textcaptcha.yml` file. Use this
|
106
|
+
rake task to add one from a
|
107
|
+
[template](https://github.com/matthutchinson/acts_as_textcaptcha/blob/master/lib/acts_as_textcaptcha/textcaptcha_config.rb):
|
102
108
|
|
103
|
-
|
104
|
-
super && user.admin?
|
105
|
-
end
|
106
|
-
end
|
109
|
+
$ bundle exec rake textcaptcha:config
|
107
110
|
|
108
|
-
|
111
|
+
**NOTE**: Any options set in models take preference over this config.
|
109
112
|
|
110
|
-
|
113
|
+
### Config without the TextCaptcha service
|
111
114
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
* *http_read_timeout* (_optional_) - Net::HTTP option, seconds to wait for one block to be read from the remote API
|
116
|
-
* *http_open_timeout* (_optional_) - Net::HTTP option, seconds to wait for the connection to open to the remote API
|
115
|
+
To use __only__ your own logic questions, omit the `api_key` and set them in the
|
116
|
+
config (see above). Multiple answers to the same question must be comma
|
117
|
+
separated e.g. `2,two` (so do not include commas in answers).
|
117
118
|
|
118
|
-
|
119
|
+
You can optionally set your own `api_endpoint` to fetch questions and answers
|
120
|
+
from. The URL must respond with a JSON object like this:
|
119
121
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
{ 'question' => 'The green hat is what color?', 'answers' => 'green' }]
|
127
|
-
end
|
122
|
+
```ruby
|
123
|
+
{
|
124
|
+
"q": "What number is 4th in the series 39, 11, 31 and nineteen?",
|
125
|
+
"a": ["1f0e3dad99908345f7439f8ffabdffc4","1d56cec552bf111de57687e4b5f8c795"]
|
126
|
+
}
|
127
|
+
```
|
128
128
|
|
129
|
-
|
129
|
+
With `"a"` being an array of answer strings, MD5'd, and lower-cased. The
|
130
|
+
`api_key` option is ignored if an `api_endpoint` is set.
|
130
131
|
|
131
|
-
|
132
|
-
config/textcaptcha.yml file. The config file must have an `api_key` defined
|
133
|
-
and/or an array of questions and answers. Any options defined inline in model
|
134
|
-
classes take preference over the global configuration in textcaptcha.yml.
|
132
|
+
### Toggling TextCaptcha
|
135
133
|
|
136
|
-
|
137
|
-
|
138
|
-
|
134
|
+
Enable or disable captchas by overriding the `perform_textcaptcha?` method (in
|
135
|
+
models). By default the method checks if the object is a new (unsaved) record.
|
136
|
+
So spam protection is __only__ enabled for creating new records (not updating).
|
139
137
|
|
140
|
-
|
138
|
+
Here's an example overriding the default behaviour but maintaining the new
|
139
|
+
record check.
|
141
140
|
|
142
|
-
|
141
|
+
```ruby
|
142
|
+
class Comment < ApplicationRecord
|
143
|
+
acts_as_textcaptcha :api_key => 'TEXTCAPTCHA_API_IDENTITY'
|
143
144
|
|
144
|
-
|
145
|
-
|
145
|
+
def perform_textcaptcha?
|
146
|
+
super && user.admin?
|
147
|
+
end
|
148
|
+
end
|
149
|
+
```
|
146
150
|
|
147
151
|
## Translations
|
148
152
|
|
149
|
-
The
|
150
|
-
English). Unfortunately at present, the TextCaptcha web service only provides
|
151
|
-
logic questions in English.
|
152
|
-
|
153
|
-
en:
|
154
|
-
activerecord:
|
155
|
-
errors:
|
156
|
-
models:
|
157
|
-
comment:
|
158
|
-
attributes:
|
159
|
-
textcaptcha_answer:
|
160
|
-
incorrect: "is incorrect, try another question instead"
|
161
|
-
expired: "was not submitted quickly enough, try another question instead"
|
162
|
-
activemodel:
|
163
|
-
attributes:
|
164
|
-
comment:
|
165
|
-
textcaptcha_answer: "TextCaptcha answer"
|
166
|
-
|
167
|
-
## Without ActiveRecord
|
168
|
-
|
169
|
-
It is possible to use this gem without ActiveRecord. As an example, take a look at the
|
170
|
-
[Contact](https://github.com/matthutchinson/acts_as_textcaptcha/blob/master/test/test_models.rb#L44)
|
171
|
-
model used in the test suite
|
172
|
-
[here](https://github.com/matthutchinson/acts_as_textcaptcha/blob/master/test/test_models.rb#L44).
|
173
|
-
|
174
|
-
## Testing and docs
|
175
|
-
|
176
|
-
In development you can run the tests and rdoc tasks like so;
|
177
|
-
|
178
|
-
* `rake test` (all tests)
|
179
|
-
* `appraisal rake test` (all tests with all gemfile variations)
|
180
|
-
* `appraisal rails-3 rake test` (all tests using a specific gemfile)
|
181
|
-
* `rake rdoc` (generate docs)
|
182
|
-
|
183
|
-
This gem uses [appraisal](https://github.com/thoughtbot/appraisal) to run the
|
184
|
-
test suite with multiple versions of Rails.
|
185
|
-
|
186
|
-
## What does the code do?
|
187
|
-
|
188
|
-
The gem contains two parts, a module for your ActiveRecord models, and a single
|
189
|
-
view helper method. The ActiveRecord module makes use of two futher classes,
|
190
|
-
[TextcaptchaApi](https://github.com/matthutchinson/acts_as_textcaptcha/blob/master/lib/acts_as_textcaptcha/textcaptcha_api.rb)
|
191
|
-
and
|
192
|
-
[TextcaptchaCache](https://github.com/matthutchinson/acts_as_textcaptcha/blob/master/lib/acts_as_textcaptcha/textcaptcha_cache.rb).
|
193
|
-
|
194
|
-
A call to `@model.textcaptcha` in your controller will query the TextCaptcha web
|
195
|
-
service. A GET request is made with Net::HTTP and parsed using the default Rails
|
196
|
-
`ActiveSupport::XMLMini` backend. A textcaptcha_question and a random cache key
|
197
|
-
are assigned to the record. An array of possible answers is also stored in the
|
198
|
-
TextcaptchaCache with this random key. The cached answers have (by default) a 10
|
199
|
-
minute TTL in your cache. If your forms take more than 10 minutes to be
|
200
|
-
completed you can adjust this value setting the `cache_expiry_minutes` option.
|
201
|
-
Internally TextcaptchaCache wraps
|
202
|
-
[Rails.cache](http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html)
|
203
|
-
and all cache keys are name spaced.
|
204
|
-
|
205
|
-
On saving, `validate_textcaptcha` is called on @model.validate checking that the
|
206
|
-
`@model.textcaptcha_answer` matches one of the possible answers (retrieved from
|
207
|
-
the cache). By default, this validation is _only_ carried out on new records,
|
208
|
-
i.e. never on update, only on create. All attempted answers are case-insensitive
|
209
|
-
and have trailing/leading white-space removed.
|
210
|
-
|
211
|
-
Regardless of a correct, or incorrect answer the possible answers are cleared
|
212
|
-
from the cache and a new random key is generated and assigned. An incorrect
|
213
|
-
answer will cause a new question to be prompted. After one correct answer, the
|
214
|
-
answer and a mutating key are sent on further form requests, and no question is
|
215
|
-
presented in the form.
|
216
|
-
|
217
|
-
If an error or timeout occurs during API fetching, ActsAsTextcaptcha will fall
|
218
|
-
back to choose a random logic question defined in your options (see above). If
|
219
|
-
the web service fails or no API key is specified AND no alternate questions are
|
220
|
-
configured, the @model will not require textcaptcha checking and will pass as
|
221
|
-
valid.
|
222
|
-
|
223
|
-
For more details on the code please check the
|
224
|
-
[documentation](http://rdoc.info/projects/matthutchinson/acts_as_textcaptcha).
|
225
|
-
Tests are written with [MiniTest](https://rubygems.org/gems/minitest). Pull
|
226
|
-
requests and bug reports are welcome.
|
153
|
+
The following strings are translatable (with Rails I18n translations):
|
227
154
|
|
228
|
-
|
155
|
+
```yaml
|
156
|
+
en:
|
157
|
+
activerecord:
|
158
|
+
errors:
|
159
|
+
models:
|
160
|
+
comment:
|
161
|
+
attributes:
|
162
|
+
textcaptcha_answer:
|
163
|
+
incorrect: "is incorrect, try another question instead"
|
164
|
+
expired: "was not submitted quickly enough, try another question instead"
|
165
|
+
activemodel:
|
166
|
+
attributes:
|
167
|
+
comment:
|
168
|
+
textcaptcha_answer: "TextCaptcha answer"
|
169
|
+
```
|
170
|
+
|
171
|
+
**NOTE**: The textcaptcha.com API only provides logic questions in English.
|
172
|
+
|
173
|
+
## Handling Errors
|
174
|
+
|
175
|
+
The API may be unresponsive or return an unexpected response. If you've set
|
176
|
+
`raise_errors: true`, consider handling these errors:
|
177
|
+
|
178
|
+
* `ActsAsTextcaptcha::ResponseError`
|
179
|
+
* `ActsAsTextcaptcha::ParseError`
|
180
|
+
* `ActsAsTextcaptcha::ApiKeyError`
|
181
|
+
|
182
|
+
## Development
|
183
|
+
|
184
|
+
Check out this repo and run `bin/setup`, this will install gem dependencies and
|
185
|
+
generate docs. Use `bundle exec rake` to run tests and generate a coverage
|
186
|
+
report.
|
187
|
+
|
188
|
+
You can also run `bin/console` for an interactive prompt to experiment with the
|
189
|
+
code.
|
190
|
+
|
191
|
+
## Tests
|
192
|
+
|
193
|
+
MiniTest is used for testing. Run the test suite with:
|
194
|
+
|
195
|
+
$ rake test
|
196
|
+
|
197
|
+
This gem uses [appraisal](https://github.com/thoughtbot/appraisal) to test
|
198
|
+
against multiple versions of Rails.
|
229
199
|
|
230
|
-
|
200
|
+
* `appraisal rake test` (run tests against all Gemfile variations)
|
201
|
+
* `appraisal rails-3 rake test` (run tests against a specific Gemfile)
|
231
202
|
|
232
|
-
|
233
|
-
* [Rails.cache](http://guides.rubyonrails.org/caching_with_rails.html#cache-stores) - a basic cache configuration is necessary
|
234
|
-
* [Ruby](http://ruby-lang.org/) >= 2.1
|
203
|
+
## Docs
|
235
204
|
|
236
|
-
|
237
|
-
[TextcaptchaCache](https://github.com/matthutchinson/acts_as_textcaptcha/blob/master/lib/acts_as_textcaptcha/textcaptcha_cache.rb)
|
238
|
-
class directly wraps the
|
239
|
-
[Rails.cache](http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html).
|
240
|
-
An alternative TextcaptchaCache implementation will be necessary if
|
241
|
-
`Rails.cache` is not available.
|
205
|
+
Generate docs for this gem with:
|
242
206
|
|
207
|
+
$ rake rdoc
|
243
208
|
|
244
|
-
##
|
209
|
+
## Troubles?
|
245
210
|
|
246
|
-
|
247
|
-
|
248
|
-
|
211
|
+
If you think something is broken or missing, please raise a new
|
212
|
+
[issue](https://github.com/matthutchinson/acts_as_textcaptcha/issues). Please
|
213
|
+
remember to check it hasn't already been raised.
|
249
214
|
|
250
|
-
|
251
|
-
gem 'acts_as_textcaptcha', '=4.0.0'
|
215
|
+
## Contributing
|
252
216
|
|
253
|
-
|
254
|
-
|
217
|
+
Bug [reports](https://github.com/matthutchinson/acts_as_textcaptcha/issues) and
|
218
|
+
[pull requests](https://github.com/matthutchinson/acts_as_textcaptcha/pulls) are
|
219
|
+
welcome on GitHub. When submitting pull requests, remember to add tests covering
|
220
|
+
any new behaviour, and ensure all tests are passing on
|
221
|
+
[Travis](https://travis-ci.org/matthutchinson/acts_as_textcaptcha). Read the
|
222
|
+
[contributing
|
223
|
+
guidelines](https://github.com/matthutchinson/acts_as_textcaptcha/blob/master/CONTRIBUTING.md)
|
224
|
+
for more details.
|
255
225
|
|
256
|
-
|
257
|
-
[
|
258
|
-
|
226
|
+
This project is intended to be a safe, welcoming space for collaboration, and
|
227
|
+
contributors are expected to adhere to the [Contributor
|
228
|
+
Covenant](http://contributor-covenant.org) code of conduct. See
|
229
|
+
[here](https://github.com/matthutchinson/acts_as_textcaptcha/blob/master/CODE_OF_CONDUCT.md)
|
230
|
+
for more details.
|
231
|
+
|
232
|
+
## Todo
|
233
|
+
|
234
|
+
* Allow translatable user supplied questions and answers in config
|
235
|
+
* Allow `Net::HTTP` to be swapped out for any another HTTP client.
|
236
|
+
|
237
|
+
## License
|
238
|
+
|
239
|
+
The code is available as open source under the terms of
|
240
|
+
[LGPL-3](https://opensource.org/licenses/LGPL-3.0).
|
241
|
+
|
242
|
+
## Who's who?
|
243
|
+
|
244
|
+
* [ActsAsTextcaptcha](http://github.com/matthutchinson/acts_as_textcaptcha) and [little robot drawing](http://www.flickr.com/photos/hiddenloop/4541195635/) by [Matthew Hutchinson](http://matthewhutchinson.net)
|
245
|
+
* [TextCaptcha](http://textcaptcha.com) API and service by [Rob Tuley](https://twitter.com/robtuley)
|
259
246
|
|
260
247
|
## Links
|
261
248
|
|
262
249
|
* [Demo](https://acts-as-textcaptcha-demo.herokuapp.com)
|
263
250
|
* [Travis CI](http://travis-ci.org/#!/matthutchinson/acts_as_textcaptcha)
|
264
251
|
* [Maintainability](https://codeclimate.com/github/matthutchinson/acts_as_textcaptcha/maintainability)
|
252
|
+
* [Test Coverage](https://codeclimate.com/github/matthutchinson/acts_as_textcaptcha/test_coverage)
|
265
253
|
* [RDoc](http://rdoc.info/projects/matthutchinson/acts_as_textcaptcha)
|
266
254
|
* [Wiki](http://wiki.github.com/matthutchinson/acts_as_textcaptcha/)
|
267
255
|
* [Issues](http://github.com/matthutchinson/acts_as_textcaptcha/issues)
|
268
256
|
* [Report a bug](http://github.com/matthutchinson/acts_as_textcaptcha/issues/new)
|
269
257
|
* [Gem](http://rubygems.org/gems/acts_as_textcaptcha)
|
270
258
|
* [GitHub](http://github.com/matthutchinson/acts_as_textcaptcha)
|
271
|
-
|
272
|
-
## Who's who?
|
273
|
-
|
274
|
-
* [ActsAsTextcaptcha](http://github.com/matthutchinson/acts_as_textcaptcha) and [little robot drawing](http://www.flickr.com/photos/hiddenloop/4541195635/) by [Matthew Hutchinson](http://matthewhutchinson.net)
|
275
|
-
* [TextCaptcha](http://textcaptcha.com) API and service by [Rob Tuley](https://twitter.com/robtuley)
|
276
|
-
|
277
|
-
## Usage
|
278
|
-
|
279
|
-
This gem is used in a number of production websites and apps. It was originally
|
280
|
-
extracted from code developed for [Bugle](http://bugleblogs.com). If you're
|
281
|
-
happily using acts_as_textcaptcha in production, please let me know and I'll add
|
282
|
-
you to this list!
|
283
|
-
|
284
|
-
* [matthewhutchinson.net](http://matthewhutchinson.net)
|
285
|
-
* [pmFAQtory.com](http://pmfaqtory.com)
|
286
|
-
* [The FAQtory](http://faqtoryapp.com)
|
287
|
-
* [DPT Watch, San Francisco](http://www.dptwatch.com)
|