tokens 0.1.0
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/README.rdoc +122 -0
- data/Rakefile +19 -0
- data/init.rb +1 -0
- data/lib/tokens/string_ext.rb +7 -0
- data/lib/tokens/token.rb +19 -0
- data/lib/tokens/version.rb +10 -0
- data/lib/tokens.rb +115 -0
- data/spec/schema.rb +18 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/tokens_spec.rb +164 -0
- metadata +73 -0
data/README.rdoc
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
= Tokens
|
2
|
+
|
3
|
+
== Usage
|
4
|
+
|
5
|
+
=== Installation
|
6
|
+
|
7
|
+
You can use I18n-JS as plugin and gem. Choose what's best for you!
|
8
|
+
|
9
|
+
script/plugin install git://github.com/fnando/has_tokens.git
|
10
|
+
|
11
|
+
or
|
12
|
+
|
13
|
+
gem install tokens
|
14
|
+
|
15
|
+
=== Setting up
|
16
|
+
|
17
|
+
Create a migration file with <tt>script/generate migration create_tokens</tt> or <tt>rails generate migration tokens</tt>.
|
18
|
+
|
19
|
+
class CreateTokens < ActiveRecord::Migration
|
20
|
+
def self.up
|
21
|
+
create_table :tokens do |t|
|
22
|
+
t.integer :tokenizable_id, :null => false
|
23
|
+
t.string :tokenizable_type, :name, :null => false
|
24
|
+
t.string :token, :limit => 40, :null => false
|
25
|
+
t.text :data, :null => true
|
26
|
+
t.datetime :expires_at, :null => true
|
27
|
+
t.datetime :created_at
|
28
|
+
end
|
29
|
+
|
30
|
+
add_index :tokens, :tokenizable_type
|
31
|
+
add_index :tokens, :tokenizable_id
|
32
|
+
add_index :tokens, :token
|
33
|
+
add_index :tokens, :expires_at
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.down
|
37
|
+
drop_table :tokens
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
Run migrations with <tt>rake db:migrate</tt>. Add the method call
|
42
|
+
<tt>has_tokens</tt> to your model and be happy!
|
43
|
+
|
44
|
+
class User < ActiveRecord::Base
|
45
|
+
has_tokens
|
46
|
+
end
|
47
|
+
|
48
|
+
# create a new token; remember that the object need to be saved before creating
|
49
|
+
# the token because it depends on the id
|
50
|
+
user = User.create(:login => "fnando")
|
51
|
+
|
52
|
+
# uses the default expires_at (2 days from now)
|
53
|
+
user.add_token(:activate)
|
54
|
+
|
55
|
+
# uses custom expires_at
|
56
|
+
user.add_token(:activate, :expires_at => 10.days.from_now)
|
57
|
+
|
58
|
+
# uses the default size (12 characters)
|
59
|
+
user.add_token(:activate)
|
60
|
+
|
61
|
+
# uses custom size (up to 32)
|
62
|
+
user.add_token(:activate, :size => 20)
|
63
|
+
|
64
|
+
# create token with arbitrary data
|
65
|
+
data = {:action => 'do something'}
|
66
|
+
user.add_token(:activate, :data => data.to_json)
|
67
|
+
|
68
|
+
# find token by name
|
69
|
+
user.find_token_by_name(:reset_account)
|
70
|
+
|
71
|
+
# find token by hash
|
72
|
+
user.find_token("ea2f14aeac40")
|
73
|
+
|
74
|
+
# check if a token has expired
|
75
|
+
user.tokens.first.expired?
|
76
|
+
|
77
|
+
# find user by token
|
78
|
+
User.find_by_token(:activate, "ea2f14aeac40")
|
79
|
+
|
80
|
+
# remove all expired tokens except those with NULL values
|
81
|
+
Token.delete_expired
|
82
|
+
|
83
|
+
# generate a token as string, without saving it
|
84
|
+
User.generate_token
|
85
|
+
|
86
|
+
# remove a token by its name
|
87
|
+
user.remove_token(:activate)
|
88
|
+
|
89
|
+
# find user by token
|
90
|
+
User.find_by_token(:activate, 'ea2f14aeac40')
|
91
|
+
|
92
|
+
# find user by valid token (same name, same hash, not expired)
|
93
|
+
User.find_by_valid_token(:activate, 'ea2f14aeac40')
|
94
|
+
|
95
|
+
# find a token using class scope
|
96
|
+
User.find_token(:activate, 'ea2f14aeac40')
|
97
|
+
|
98
|
+
# Token hash
|
99
|
+
token.to_s #=> ea2f14aeac40
|
100
|
+
|
101
|
+
== License
|
102
|
+
|
103
|
+
Copyright (c) 2008 Nando Vieira, released under the MIT license
|
104
|
+
|
105
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
106
|
+
a copy of this software and associated documentation files (the
|
107
|
+
"Software"), to deal in the Software without restriction, including
|
108
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
109
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
110
|
+
permit persons to whom the Software is furnished to do so, subject to
|
111
|
+
the following conditions:
|
112
|
+
|
113
|
+
The above copyright notice and this permission notice shall be
|
114
|
+
included in all copies or substantial portions of the Software.
|
115
|
+
|
116
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
117
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
118
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
119
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
120
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
121
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
122
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require "lib/tokens/version"
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'jeweler'
|
5
|
+
|
6
|
+
JEWEL = Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "tokens"
|
8
|
+
gem.email = "fnando.vieira@gmail.com"
|
9
|
+
gem.homepage = "http://github.com/fnando/has_tokens"
|
10
|
+
gem.authors = ["Nando Vieira"]
|
11
|
+
gem.version = SimplesIdeias::Tokens::Version::STRING
|
12
|
+
gem.summary = "Generate named tokens on your ActiveRecord models."
|
13
|
+
gem.files = FileList["README.rdoc", "init.rb", "{lib,spec,source}/**/*", "Rakefile"]
|
14
|
+
end
|
15
|
+
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError => e
|
18
|
+
puts "[JEWELER] You can't build a gem until you install jeweler with `gem install jeweler`"
|
19
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'tokens'
|
data/lib/tokens/token.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
class Token < ActiveRecord::Base
|
2
|
+
belongs_to :tokenizable, :polymorphic => true
|
3
|
+
|
4
|
+
def hash
|
5
|
+
token
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_s
|
9
|
+
hash
|
10
|
+
end
|
11
|
+
|
12
|
+
def expired?
|
13
|
+
expires_at && expires_at < Time.now
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.delete_expired
|
17
|
+
delete_all ["expires_at < ? AND expires_at IS NOT NULL", Time.now]
|
18
|
+
end
|
19
|
+
end
|
data/lib/tokens.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require "active_record"
|
2
|
+
require "digest/sha1"
|
3
|
+
require "tokens/string_ext"
|
4
|
+
require "tokens/token"
|
5
|
+
|
6
|
+
module SimplesIdeias
|
7
|
+
module Tokens #:nodoc:
|
8
|
+
def self.included(base)
|
9
|
+
base.extend ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def has_tokens
|
14
|
+
write_inheritable_attribute(:has_tokens_options, {
|
15
|
+
:token_type => ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s
|
16
|
+
})
|
17
|
+
|
18
|
+
class_inheritable_reader :has_tokens_options
|
19
|
+
|
20
|
+
has_many :tokens, :as => :tokenizable, :dependent => :destroy
|
21
|
+
include SimplesIdeias::Tokens::InstanceMethods
|
22
|
+
end
|
23
|
+
|
24
|
+
def generate_token(seed, size)
|
25
|
+
validity = Proc.new { |token| Token.find(:first, :conditions => {:token => token}).nil? }
|
26
|
+
|
27
|
+
begin
|
28
|
+
seed = Digest::SHA1.hexdigest(seed)
|
29
|
+
token = Digest::SHA1.hexdigest(seed)[0, size]
|
30
|
+
end while !validity.call(token)
|
31
|
+
|
32
|
+
token
|
33
|
+
end
|
34
|
+
|
35
|
+
# Find a token
|
36
|
+
# User.find_token(:activation, 'abcdefg')
|
37
|
+
# User.find_token(:name => activation, :token => 'abcdefg')
|
38
|
+
# User.find_token(:name => activation, :token => 'abcdefg', :tokenizable_id => 1)
|
39
|
+
def find_token(*args)
|
40
|
+
unless (options = args.first).is_a?(Hash)
|
41
|
+
options = {:name => args.first, :token => args.last.to_s}
|
42
|
+
end
|
43
|
+
|
44
|
+
options[:name] = options[:name].to_s
|
45
|
+
options.merge!({:tokenizable_type => has_tokens_options[:token_type]})
|
46
|
+
Token.find(:first, :conditions => options)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Find object by token
|
50
|
+
# User.find_by_token(:activation, 'abcdefg')
|
51
|
+
def find_by_token(name, token)
|
52
|
+
t = find_token(:name => name.to_s, :token => token)
|
53
|
+
return nil unless t
|
54
|
+
t.tokenizable
|
55
|
+
end
|
56
|
+
|
57
|
+
# Find object by valid token (same name, same hash, not expired)
|
58
|
+
# User.find_by_valid_token(:activation, 'abcdefg')
|
59
|
+
def find_by_valid_token(name, token)
|
60
|
+
t = find_token(:name => name.to_s, :token => token)
|
61
|
+
return nil unless t && !t.expired? && t.hash == token
|
62
|
+
t.tokenizable
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
module InstanceMethods
|
67
|
+
# Object has a valid token (same name, same hash, not expired)
|
68
|
+
# @user.valid_token?(:activation, 'abcdefg')
|
69
|
+
def valid_token?(name, token)
|
70
|
+
t = find_token_by_name(name)
|
71
|
+
!!(t && !t.expired? && t.hash == token)
|
72
|
+
end
|
73
|
+
|
74
|
+
def remove_token(name)
|
75
|
+
Token.delete_all([
|
76
|
+
"tokenizable_id = ? AND tokenizable_type = ? AND name = ?",
|
77
|
+
self.id,
|
78
|
+
self.class.has_tokens_options[:token_type],
|
79
|
+
name.to_s
|
80
|
+
])
|
81
|
+
end
|
82
|
+
|
83
|
+
def add_token(name, options={})
|
84
|
+
options = {
|
85
|
+
:expires_at => 2.days.from_now,
|
86
|
+
:size => 12,
|
87
|
+
:data => nil
|
88
|
+
}.merge(options)
|
89
|
+
|
90
|
+
remove_token(name)
|
91
|
+
|
92
|
+
seed = "--#{self.id}--#{self.object_id}--#{Time.now}--"
|
93
|
+
|
94
|
+
self.tokens.create(
|
95
|
+
:name => name.to_s,
|
96
|
+
:token => self.class.generate_token(seed, options[:size]),
|
97
|
+
:expires_at => options[:expires_at],
|
98
|
+
:data => options[:data]
|
99
|
+
)
|
100
|
+
end
|
101
|
+
|
102
|
+
def find_token_by_name(name)
|
103
|
+
self.tokens.find_by_name(name.to_s)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Find a token
|
107
|
+
# @user.find_token(:activation, 'abcdefg')
|
108
|
+
def find_token(name, token)
|
109
|
+
self.class.find_token(:tokenizable_id => self.id, :name => name.to_s, :token => token)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
ActiveRecord::Base.send(:include, SimplesIdeias::Tokens)
|
data/spec/schema.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
ActiveRecord::Schema.define(:version => 0) do
|
2
|
+
create_table :users do |t|
|
3
|
+
t.string :name
|
4
|
+
end
|
5
|
+
|
6
|
+
create_table :posts do |t|
|
7
|
+
t.string :title
|
8
|
+
end
|
9
|
+
|
10
|
+
create_table :tokens do |t|
|
11
|
+
t.integer :tokenizable_id, :null => false
|
12
|
+
t.string :tokenizable_type, :name, :null => false
|
13
|
+
t.string :token, :limit => 40, :null => false
|
14
|
+
t.text :data, :null => true
|
15
|
+
t.datetime :expires_at, :null => true
|
16
|
+
t.datetime :created_at
|
17
|
+
end
|
18
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + "/../lib"
|
2
|
+
|
3
|
+
require "spec"
|
4
|
+
require "ruby-debug"
|
5
|
+
require "active_record"
|
6
|
+
require "tokens"
|
7
|
+
|
8
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ":memory:")
|
9
|
+
|
10
|
+
load('schema.rb')
|
11
|
+
|
12
|
+
class Object
|
13
|
+
def self.unset_class(*args)
|
14
|
+
class_eval do
|
15
|
+
args.each do |klass|
|
16
|
+
eval(klass) rescue nil
|
17
|
+
remove_const(klass) if const_defined?(klass)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
alias :doing :lambda
|
data/spec/tokens_spec.rb
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
class User < ActiveRecord::Base
|
4
|
+
has_tokens
|
5
|
+
end
|
6
|
+
|
7
|
+
class Post < ActiveRecord::Base
|
8
|
+
has_tokens
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "has_tokens" do
|
12
|
+
before(:each) do
|
13
|
+
User.delete_all
|
14
|
+
Post.delete_all
|
15
|
+
|
16
|
+
@user = User.create(:name => "Homer")
|
17
|
+
@another_user = User.create(:name => "Bart")
|
18
|
+
@post = Post.create(:title => "How to make donuts")
|
19
|
+
@expire = 3.days.from_now
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "- token" do
|
23
|
+
it "should be created" do
|
24
|
+
doing { @user.add_token(:uid) }.should change(Token, :count)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should be created for different users" do
|
28
|
+
@user.add_token(:uid).should be_valid
|
29
|
+
@another_user.add_token(:uid).should be_valid
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should be created with expiration date" do
|
33
|
+
@user.add_token(:uid, :expires_at => @expire).expires_at.should == @expire
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should be created with additional data" do
|
37
|
+
@user.add_token(:uid, :data => 'some value').data.should == 'some value'
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should be created with custom size" do
|
41
|
+
@user.add_token(:uid, :size => 6).hash.size.should == 6
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should find token by its name" do
|
45
|
+
token = @user.add_token(:uid)
|
46
|
+
@user.find_token_by_name(:uid).should == token
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should be nil when no token is found" do
|
50
|
+
@user.find_token(:uid, 'abcdef').should be_nil
|
51
|
+
@user.find_token_by_name(:uid).should be_nil
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should be a valid token" do
|
55
|
+
token = @user.add_token(:uid)
|
56
|
+
@user.valid_token?(:uid, token.hash).should be_true
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should not be a valid token" do
|
60
|
+
@user.valid_token?(:uid, 'invalid').should be_false
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should find token by its name and hash" do
|
64
|
+
token = @user.add_token(:uid)
|
65
|
+
@user.find_token(:uid, token.hash).should == token
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should not be expired when have no expiration date" do
|
69
|
+
@user.add_token(:uid).should_not be_expired
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should not be expired when have a future expiration date" do
|
73
|
+
@user.add_token(:uid, :expires_at => 3.days.from_now).should_not be_expired
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should be expired" do
|
77
|
+
@user.add_token(:uid, :expires_at => 3.days.ago).should be_expired
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should remove token" do
|
81
|
+
@user.add_token(:uid)
|
82
|
+
@user.remove_token(:uid).should > 0
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should not remove other users tokens" do
|
86
|
+
@user.add_token(:uid)
|
87
|
+
@another_user.add_token(:uid)
|
88
|
+
|
89
|
+
@user.remove_token(:uid)
|
90
|
+
|
91
|
+
@user.find_token_by_name(:uid).should be_nil
|
92
|
+
@another_user.find_token_by_name(:uid).should be_an_instance_of(Token)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should not be duplicated" do
|
96
|
+
@user.add_token(:uid)
|
97
|
+
@user.add_token(:uid)
|
98
|
+
|
99
|
+
@user.tokens.find_all_by_name('uid').size.should == 1
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should have tokens association" do
|
104
|
+
doing { @user.tokens }.should_not raise_error
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should remove all expired tokens" do
|
108
|
+
doing {
|
109
|
+
%w(uid activation_code reset_password_code).each do |name|
|
110
|
+
@user.add_token(name, :expires_at => 3.days.ago)
|
111
|
+
end
|
112
|
+
}.should change(Token, :count).by(3)
|
113
|
+
|
114
|
+
Token.delete_expired.should == 3
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should generate token without saving it" do
|
118
|
+
doing {
|
119
|
+
User.generate_token(Time.now.to_s, 32)
|
120
|
+
}.should_not change(Token, :count)
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should generate token with custom size" do
|
124
|
+
User.generate_token(Time.now.to_s, 8).size.should == 8
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should alias token method" do
|
128
|
+
token = @user.add_token(:uid)
|
129
|
+
token.hash.should == token.token
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should find user by token" do
|
133
|
+
token = @user.add_token(:uid)
|
134
|
+
User.find_by_token(:uid, token.hash).should == @user
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should return user by its valid token without expiration time" do
|
138
|
+
token = @user.add_token(:uid)
|
139
|
+
User.find_by_valid_token(:uid, token.hash).should == @user
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should return user by its valid token with expiration time" do
|
143
|
+
token = @user.add_token(:uid, :expires_at => @expire)
|
144
|
+
User.find_by_valid_token(:uid, token.hash).should == @user
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should find token using class method with one argument (hash only)" do
|
148
|
+
token = @user.add_token(:uid)
|
149
|
+
User.find_token(:name => :uid, :token => token.hash).should == token
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should not conflict with other models" do
|
153
|
+
user_token = @user.add_token(:uid)
|
154
|
+
post_token = @post.add_token(:uid)
|
155
|
+
|
156
|
+
User.find_token(post_token.to_s).should == nil
|
157
|
+
User.find_token(:name => :uid)
|
158
|
+
end
|
159
|
+
|
160
|
+
it "to_s should return hash" do
|
161
|
+
token = @user.add_token(:uid)
|
162
|
+
token.to_s.should == token.hash
|
163
|
+
end
|
164
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tokens
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Nando Vieira
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-07-09 00:00:00 -03:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description:
|
22
|
+
email: fnando.vieira@gmail.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files:
|
28
|
+
- README.rdoc
|
29
|
+
files:
|
30
|
+
- README.rdoc
|
31
|
+
- Rakefile
|
32
|
+
- init.rb
|
33
|
+
- lib/tokens.rb
|
34
|
+
- lib/tokens/string_ext.rb
|
35
|
+
- lib/tokens/token.rb
|
36
|
+
- lib/tokens/version.rb
|
37
|
+
- spec/schema.rb
|
38
|
+
- spec/spec_helper.rb
|
39
|
+
- spec/tokens_spec.rb
|
40
|
+
has_rdoc: true
|
41
|
+
homepage: http://github.com/fnando/has_tokens
|
42
|
+
licenses: []
|
43
|
+
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options:
|
46
|
+
- --charset=UTF-8
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
segments:
|
54
|
+
- 0
|
55
|
+
version: "0"
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.3.6
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: Generate named tokens on your ActiveRecord models.
|
70
|
+
test_files:
|
71
|
+
- spec/schema.rb
|
72
|
+
- spec/spec_helper.rb
|
73
|
+
- spec/tokens_spec.rb
|