catptcha 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,20 +9,29 @@ This is still experimental and incomplete. Don't use it.
9
9
 
10
10
  ## Installation
11
11
 
12
- Add the gem to your Gemfile and optionally require the rake task.
12
+ From within your Rails application:
13
13
 
14
- load 'tasks/catptcha.rake'
14
+ Add the gem to your Gemfile and load the rake tasks.
15
+
16
+ echo "gem 'catptcha'" >> Gemfile
17
+ echo "load 'tasks/catptcha.rake'" >> Rakefile
15
18
 
16
19
  Put your flickr keys in config/flickr.yml, see flickr.example.yml.
17
20
 
18
- Generate the migration:
21
+ Generate and run the migration.
19
22
 
20
- ./script/generate catptcha_migration add_catptcha_tables
23
+ ruby script/generate catptcha_migration add_catptcha_tables
24
+ rake db:migrate
21
25
 
22
- Seed the images:
26
+ Seed the images.
23
27
 
24
28
  rake catptcha:seed
25
29
 
30
+ Run the tests.
31
+
32
+ rake db:test:prepare
33
+ rake catptcha:test
34
+
26
35
  ## Author
27
36
 
28
37
  Copyright (c) 2012 ENTP, released under the MIT license
data/Rakefile CHANGED
@@ -1,23 +1,10 @@
1
- require 'rake'
2
- require 'rake/testtask'
3
- require 'rdoc/task'
4
1
 
5
- desc 'Default: run unit tests.'
6
- task :default => :test
2
+ desc 'Default: build the gem'
3
+ task :default => :gem
7
4
 
8
- desc 'Test the catptcha plugin.'
9
- Rake::TestTask.new(:test) do |t|
10
- t.libs << 'lib'
11
- t.libs << 'test'
12
- t.pattern = 'test/**/*_test.rb'
13
- t.verbose = true
5
+ desc 'Build the gem'
6
+ task :gem do
7
+ sh 'gem build catptcha.gemspec'
14
8
  end
15
9
 
16
- desc 'Generate documentation for the catptcha plugin.'
17
- RDoc::Task.new(:rdoc) do |rdoc|
18
- rdoc.rdoc_dir = 'rdoc'
19
- rdoc.title = 'Catptcha'
20
- rdoc.options << '--line-numbers' << '--inline-source'
21
- rdoc.rdoc_files.include('README')
22
- rdoc.rdoc_files.include('lib/**/*.rb')
23
- end
10
+
data/catptcha.gemspec CHANGED
@@ -4,12 +4,13 @@ require 'catptcha'
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "catptcha"
6
6
  s.version = Catptcha::VERSION
7
- s.summary = 'Visual catptcha plugin for Rails 2.3'
7
+ s.summary = 'Visual captcha plugin for Rails 2.3'
8
8
  s.description = <<-EOD
9
9
  Choose the kitten!
10
10
  EOD
11
11
  s.add_dependency "commonthread-flickr_fu", "0.3.0"
12
12
  s.add_dependency "activerecord", "~>2.3"
13
+ s.add_development_dependency "rr"
13
14
  s.authors = ['Zack Hobson']
14
15
  s.files = `git ls-files`.split("\n")
15
16
  s.test_files = `git ls-files -- test/*`.split("\n")
@@ -2,18 +2,23 @@ class <%= class_name %> < ActiveRecord::Migration
2
2
  def self.up
3
3
  create_table :catptcha_photos do |t|
4
4
  t.belongs_to :photo_group
5
+ t.string :key
5
6
  t.string :url
6
- t.index :catptcha_group_id
7
7
  end
8
+ add_index :catptcha_photos, :photo_group_id
9
+ add_index :catptcha_photos, :key, :unique => true
10
+
8
11
  create_table :catptcha_photo_groups do |t|
9
12
  t.string :name
10
13
  t.string :flickr_group_id
11
14
  t.timestamp :photos_updated_at
12
15
  t.integer :photos_count
13
16
  t.boolean :kittens
14
- t.index :kittens
15
17
  end
18
+ add_index :catptcha_photo_groups, :flickr_group_id, :unique => true
19
+ add_index :catptcha_photo_groups, :kittens
16
20
  end
21
+
17
22
  def self.down
18
23
  drop_table :catptcha_photos
19
24
  drop_table :catptcha_photo_groups
@@ -2,4 +2,12 @@
2
2
  class Catptcha::Photo < ActiveRecord::Base
3
3
  set_table_name "catptcha_photos"
4
4
  belongs_to :photo_group, :counter_cache => :photos_count, :class_name => 'Catptcha::PhotoGroup'
5
+
6
+ before_validation_on_create :generate_key
7
+ validates_uniqueness_of :key, :on => :create
8
+ validates_presence_of :photo_group_id
9
+
10
+ def generate_key
11
+ self.key = Digest::SHA1.hexdigest(url)
12
+ end
5
13
  end
@@ -4,62 +4,24 @@
4
4
  class Catptcha::PhotoGroup < ActiveRecord::Base
5
5
  set_table_name 'catptcha_photo_groups'
6
6
 
7
+ validates_presence_of :name, :flickr_group_id
7
8
  validates_uniqueness_of :name
8
- validates_uniqueness_of :flickr_group_id
9
9
 
10
10
  has_many :photos, :class_name => 'Catptcha::Photo'
11
11
  named_scope :kittens, :conditions => {:kittens => true}
12
12
  named_scope :not_kittens, :conditions => {:kittens => false}
13
13
 
14
14
  def self.photos_count
15
- all.inject(0) {|a,g| a += (g.photos_count || 0) }
15
+ sum(:photos_count)
16
16
  end
17
17
 
18
18
  def self.random count
19
- # TODO use a single query
20
- results = []
21
- count.times do
22
- conditions = ['photo_group_id in (?)', all.map(&:id)]
23
- if results.any?
24
- conditions.first << ' and catptcha_photos.id not in (?)'
25
- conditions << results.map(&:id)
26
- end
27
- results << Photo.first(
28
- :conditions => conditions,
29
- :offset => rand(photos_count).to_i
30
- )
31
- end
32
- results
33
- end
34
-
35
- def update_images flickr
36
- get_photos(flickr) do |photo|
37
- photos.create(:url => photo.url(:square))
38
- end
39
- update_attributes(:photos_updated_at => DateTime.now.utc)
40
- end
41
-
42
- # fetches up to 2000 photos
43
- def get_photos flickr
44
- params = {
45
- :group_id => flickr_group_id, :safe_search => 1, :content_type => 1,
46
- :per_page => 500, :page => 1
47
- }
48
- params[:min_upload_date] = photos_updated_at if photos_updated_at
49
-
50
- total_pages = nil
51
- loop do
52
- result = flickr.photos.search(params)
53
- result.each do |photo|
54
- yield photo
55
- end
56
- if total_pages.nil?
57
- total_pages = [result.pages, 4].min
58
- else
59
- params[:page] += 1
60
- break if params[:page] > total_pages
61
- end
62
- end
19
+ # TODO generate an identity/key column that isn't easy to guess
20
+ Catptcha::Photo.all(
21
+ :conditions => ['photo_group_id in (?)', all.map(&:id)],
22
+ :offset => rand(photos_count-count).to_i,
23
+ :limit => count
24
+ )
63
25
  end
64
26
 
65
27
  end
data/lib/catptcha/seed.rb CHANGED
@@ -14,7 +14,7 @@ module Catptcha::Seed
14
14
  @flickr ||= Flickr.new(Rails.root.join("config/flickr.yml").to_s)
15
15
  end
16
16
 
17
- def update
17
+ def update silent=false
18
18
  SEED_GROUP_KITTENS.each do |name, id|
19
19
  Catptcha::PhotoGroup.create(:name => name.to_s, :flickr_group_id => id, :kittens => true)
20
20
  end
@@ -22,8 +22,37 @@ module Catptcha::Seed
22
22
  Catptcha::PhotoGroup.create(:name => name.to_s, :flickr_group_id => id, :kittens => false)
23
23
  end
24
24
  Catptcha::PhotoGroup.all.each do |group|
25
- group.update_images flickr
25
+ puts "updating photos for `#{group.name}`..." unless silent
26
+ count = update_photos group
27
+ puts "...done, #{count} photos imported" unless silent
26
28
  end
27
29
  end
30
+
31
+ # fetches up to 2500 photos
32
+ def update_photos group
33
+ params = {
34
+ :group_id => group.flickr_group_id, :safe_search => 1, :content_type => 1,
35
+ :per_page => 500, :page => 1
36
+ }
37
+ params[:min_upload_date] = group.photos_updated_at if group.photos_updated_at
38
+
39
+ total_pages = nil
40
+ photo_count = 0
41
+ loop do
42
+ result = flickr.photos.search(params)
43
+ result.each do |photo|
44
+ group.photos.create(:url => photo.url(:square))
45
+ photo_count += 1
46
+ end
47
+ if total_pages.nil?
48
+ total_pages = [result.pages, 5].min
49
+ else
50
+ params[:page] += 1
51
+ end
52
+ break if params[:page] >= total_pages
53
+ end
54
+ group.update_attributes(:photos_updated_at => DateTime.now.utc)
55
+ photo_count
56
+ end
28
57
  end
29
58
  end
data/lib/catptcha.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Catptcha
2
- VERSION = '0.0.2'
2
+ VERSION = '0.0.3'
3
3
 
4
4
  class << self
5
5
  # Get a list of 3 kitten photos
@@ -11,9 +11,20 @@ module Catptcha
11
11
  def not_kittens
12
12
  PhotoGroup.not_kittens.random(6)
13
13
  end
14
+
15
+ def check_guesses *ids
16
+ guesses = ids.flatten.compact
17
+ if guesses.size != 3
18
+ return false
19
+ end
20
+ 3 == Photo.count(:include => 'photo_group', :conditions => [
21
+ 'catptcha_photos.id in (?) and kittens = ?', guesses, true
22
+ ])
23
+ end
14
24
  end
15
25
 
16
26
  autoload :Photo, 'catptcha/photo'
17
27
  autoload :PhotoGroup, 'catptcha/photo_group'
18
28
  autoload :Seed, 'catptcha/seed'
29
+
19
30
  end
@@ -3,5 +3,11 @@ namespace :catptcha do
3
3
  task :seed => :environment do
4
4
  Catptcha::Seed.update
5
5
  end
6
+
7
+ desc 'run catptcha plugin tests'
8
+ Rake::TestTask.new do |t|
9
+ t.test_files = FileList[File.dirname(__FILE__)+'/../../test/**/*_test.rb']
10
+ t.verbose = true
11
+ end
6
12
  end
7
13
 
@@ -0,0 +1,24 @@
1
+ require File.dirname(__FILE__)+'/test_helper.rb'
2
+
3
+ class CatptchaSeedTest < Test::Unit::TestCase
4
+ def setup
5
+ Catptcha::PhotoGroup.delete_all
6
+ Catptcha::Photo.delete_all
7
+ end
8
+
9
+ def test_seed_with_no_photos
10
+ stub(Catptcha::Seed).flickr { build_flickr_stub [] }
11
+ Catptcha::Seed.update :silent
12
+ assert_equal 3, Catptcha::PhotoGroup.count
13
+ assert_equal 0, Catptcha::Photo.count
14
+ # doesn't recreate groups
15
+ Catptcha::Seed.update :silent
16
+ assert_equal 3, Catptcha::PhotoGroup.count
17
+ end
18
+
19
+ def test_seed_with_photos
20
+ stub(Catptcha::Seed).flickr { build_flickr_stub { [ build_flickr_photo_stub ] } }
21
+ Catptcha::Seed.update :silent
22
+ assert_equal 3, Catptcha::Photo.count
23
+ end
24
+ end
@@ -0,0 +1,21 @@
1
+ require File.dirname(__FILE__)+'/test_helper.rb'
2
+
3
+ class CatptchaTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ Catptcha::Photo.delete_all
7
+ Catptcha::PhotoGroup.delete_all
8
+ kittens = Catptcha::PhotoGroup.create!(:name => 'kittens', :flickr_group_id => 'x', :kittens => true)
9
+ 3.times {|i| kittens.photos.create!(:url => "http://#{i}") }
10
+ not_kittens = Catptcha::PhotoGroup.create!(:name => 'not_kittens', :flickr_group_id => 'y', :kittens => false)
11
+ 6.times {|i| not_kittens.photos.create!(:url => "http://#{i+3}") }
12
+ end
13
+
14
+ def test_kittens
15
+ assert Catptcha.check_guesses(Catptcha.kittens.map(&:id))
16
+ end
17
+
18
+ def test_not_kittens
19
+ assert !Catptcha.check_guesses(Catptcha.not_kittens.map(&:id)[0,3])
20
+ end
21
+ end
@@ -0,0 +1,27 @@
1
+ require File.dirname(__FILE__)+'/test_helper.rb'
2
+
3
+ class PhotoGroupTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ Catptcha::Photo.delete_all
7
+ Catptcha::PhotoGroup.delete_all
8
+ kittens = Catptcha::PhotoGroup.create!(:name => 'kittens', :flickr_group_id => 'x', :kittens => true)
9
+ 3.times {|i| kittens.photos.create!(:url => "http://#{i}") }
10
+ kittens = Catptcha::PhotoGroup.create!(:name => 'more_kittens', :flickr_group_id => 'a', :kittens => true)
11
+ 3.times {|i| kittens.photos.create!(:url => "http://#{i+3}") }
12
+ not_kittens = Catptcha::PhotoGroup.create!(:name => 'tacos', :flickr_group_id => 'y', :kittens => false)
13
+ 6.times {|i| not_kittens.photos.create!(:url => "http://#{i+6}") }
14
+ not_kittens = Catptcha::PhotoGroup.create!(:name => 'yaks', :flickr_group_id => 'b', :kittens => false)
15
+ 6.times {|i| not_kittens.photos.create!(:url => "http://#{i+12}") }
16
+ end
17
+
18
+ def test_photo_count
19
+ assert_equal 6, Catptcha::PhotoGroup.kittens.photos_count
20
+ assert_equal 12, Catptcha::PhotoGroup.not_kittens.photos_count
21
+ end
22
+
23
+ def test_random
24
+ assert_equal 3, Catptcha::PhotoGroup.kittens.random(3).size
25
+ assert_equal 6, Catptcha::PhotoGroup.not_kittens.random(6).size
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ ENV['RAILS_ENV'] = 'test'
2
+ require "./config/environment"
3
+ require 'test/unit'
4
+ require 'rr'
5
+
6
+ class Test::Unit::TestCase
7
+ include RR::Adapters::RRMethods
8
+
9
+ # Stub out a flickr photo with a unique url
10
+ def build_flickr_photo_stub
11
+ @index ||= 0
12
+ @index += 1
13
+ Object.new.tap do |photo|
14
+ stub(photo).url { "http://#{@index}" }
15
+ end
16
+ end
17
+
18
+ # Stub out a flickr search query using the provided photos,
19
+ # for lazy evaluation pass the photos in a block instead
20
+ def build_flickr_stub value=nil
21
+ Object.new.tap do |flickr|
22
+ photos = Object.new
23
+ stub(flickr).photos { stub(photos).search {
24
+ (value || yield).tap {|result| stub(result).pages { 1 } }
25
+ }}
26
+ end
27
+ end
28
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: catptcha
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 25
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 2
10
- version: 0.0.2
9
+ - 3
10
+ version: 0.0.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Zack Hobson
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-05-16 00:00:00 -07:00
18
+ date: 2012-05-17 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -49,6 +49,20 @@ dependencies:
49
49
  version: "2.3"
50
50
  type: :runtime
51
51
  version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ name: rr
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ hash: 3
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ type: :development
65
+ version_requirements: *id003
52
66
  description: " Choose the kitten!\n"
53
67
  email:
54
68
  executables: []
@@ -60,7 +74,7 @@ extra_rdoc_files: []
60
74
  files:
61
75
  - .gitignore
62
76
  - MIT-LICENSE
63
- - README
77
+ - README.markdown
64
78
  - Rakefile
65
79
  - catptcha.gemspec
66
80
  - generators/catptcha_migration/catptcha_migration_generator.rb
@@ -72,6 +86,10 @@ files:
72
86
  - lib/catptcha/seed.rb
73
87
  - lib/tasks/catptcha.rake
74
88
  - rails/init.rb
89
+ - test/catptcha_seed_test.rb
90
+ - test/catptcha_test.rb
91
+ - test/photo_group_test.rb
92
+ - test/test_helper.rb
75
93
  - uninstall.rb
76
94
  has_rdoc: true
77
95
  homepage:
@@ -106,6 +124,9 @@ rubyforge_project:
106
124
  rubygems_version: 1.6.2
107
125
  signing_key:
108
126
  specification_version: 3
109
- summary: Visual catptcha plugin for Rails 2.3
110
- test_files: []
111
-
127
+ summary: Visual captcha plugin for Rails 2.3
128
+ test_files:
129
+ - test/catptcha_seed_test.rb
130
+ - test/catptcha_test.rb
131
+ - test/photo_group_test.rb
132
+ - test/test_helper.rb