model_tokenizer 1.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e7bdefea09ee395696ca8a79de783f1cd5d32bab
4
+ data.tar.gz: 470888cd2837f19c76243d6044bb812f0a889800
5
+ SHA512:
6
+ metadata.gz: 91e755690ca0e0d94da13df0405f9c96f4946507e377bec671aad21cb1439b3fb679bda0934c0e47931f104ab3f01fc2b2c097d9128142a277aeedc6bfca2769
7
+ data.tar.gz: 225694841bb5e37942302465b5177e0dc1a07726f50ff0346d3ab11bee2eff8935e1260f5b42da895d049635354c3cb111ad0cfac1e55560fa12904ecb6bada4
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in model_tokenizer.gemspec
4
+ gemspec
5
+
6
+ gem "rails"
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Adib Saad
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,89 @@
1
+ # ModelTokenizer
2
+
3
+ Generates random tokens that models can be accessed by. Instead of
4
+
5
+ ```
6
+ somesite.com/video/71589
7
+ ```
8
+
9
+ you'll get
10
+
11
+ ```
12
+ somesite.com/video/j5-drkENpSDBNRds
13
+ ```
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem 'model_tokenizer'
21
+ ```
22
+
23
+ And then execute:
24
+
25
+ $ bundle
26
+
27
+ Or install it yourself as:
28
+
29
+ $ gem install model_tokenizer
30
+
31
+ ## Usage
32
+
33
+ 1. Run
34
+
35
+ $ rails g model_tokenizer MODEL_NAME [field:type field:type ... ]
36
+
37
+ to create a new tokenized model. If the model already exists, ModelTokenizer will integrate into it by injecting the following code
38
+
39
+ ```ruby
40
+ extend ModelTokenizer
41
+ has_token
42
+ ```
43
+
44
+ The appropriate migration will also be created, which will create the ```token``` field and its associated unique index.
45
+
46
+ The default token length is 14, but you can change it (no lower than 8)
47
+
48
+ ```ruby
49
+ has_token :length => 16
50
+ ```
51
+
52
+ 2. In the model file, make sure the following line is there:
53
+
54
+ ```ruby
55
+ self.primary_key = :token
56
+ ```
57
+
58
+ The generator will automatically inject this, but if you're doing something weird that involves manually installing ModelTokenizer without using the generators, make sure the aforementioned line exists.
59
+
60
+
61
+ ## Notes
62
+
63
+ ModelTokenizer generates tokens from the following charset:
64
+
65
+ ```
66
+ a b c d e f g h i j k m n o p q r s t u v w x y z
67
+ A B C D E F G H J K L M N P R S T W X Y Z
68
+ 2 3 4 5 6 7 8 9
69
+ - _
70
+ ```
71
+
72
+ As you may have noticed, the following ambiguous characters have been removed
73
+
74
+ * Lowercase: l
75
+ * Uppercase: I, O, Q, U, V
76
+ * Numerals: 1, 0
77
+
78
+ However, the gem doesn't check for awkward tokens that could be confusing, has too many repeating characters, too many underscores/hyphens or otherwise makes someone raise an eyebrow (e.g. DXMHMHLALAH, _-aj-a2j6f-qacins-). Additionally, ModelTokenizer doesn't detect whether or not it has run out of combinations for
79
+ generating new tokens, though this will be dealt with in the future.
80
+
81
+ ModelTokenizer has been tested with Rails 3 and 4.
82
+
83
+ ## Contributing
84
+
85
+ 1. Fork it ( https://github.com/adibsaad/model_tokenizer/fork )
86
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
87
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
88
+ 4. Push to the branch (`git push origin my-new-feature`)
89
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ desc 'Test model_tokenizer.'
5
+ Rake::TestTask.new do |t|
6
+ t.libs << 'test'
7
+ t.test_files = FileList['test/*_test.rb']
8
+ t.verbose = true
9
+ end
10
+
11
+ desc "Run tests"
12
+ task :default => :test
@@ -0,0 +1,76 @@
1
+ require 'rails/generators/active_record'
2
+ require 'rails/generators/migration'
3
+
4
+ module ModelTokenizer
5
+ module Generators
6
+ class ModelTokenizerGenerator < ActiveRecord::Generators::Base
7
+ include Rails::Generators::Migration
8
+
9
+ argument :attributes, type: :array, default: [], banner: "field:type field:type"
10
+ namespace "model_tokenizer"
11
+ source_root File.expand_path("../templates", __FILE__)
12
+ desc "Creates a model with the NAME argument. "\
13
+ "If the model already exists, the appropriate code will be appended instead. "\
14
+ "In either case, the appropriate migration will be created."
15
+
16
+ def create_migration_file
17
+ if (behavior == :invoke && model_exists?) || (behavior == :revoke && migration_exists?(table_name))
18
+ migration_template "migration_existing.rb", "db/migrate/add_model_tokenizer_token_to_#{table_name}.rb"
19
+ else
20
+ migration_template "migration.rb", "db/migrate/model_tokenizer_create_#{table_name}.rb"
21
+ end
22
+ end
23
+
24
+ def generate_model
25
+ invoke "active_record:model", [name], migration: false unless model_exists? && behavior == :invoke
26
+ end
27
+
28
+ def inject_model_tokenizer_content
29
+ content = model_contents
30
+
31
+ class_path = if namespaced?
32
+ class_name.to_s.split("::")
33
+ else
34
+ [class_name]
35
+ end
36
+
37
+ indent_depth = class_path.size - 1
38
+ content = content.split("\n").map { |line| " " * indent_depth + line } .join("\n") << "\n"
39
+
40
+ inject_into_class(model_path, class_path.last, content) if model_exists?
41
+ end
42
+
43
+ private
44
+
45
+ def migration_data
46
+ <<RUBY
47
+ t.string :token, :null => false, :default => ""
48
+ RUBY
49
+ end
50
+
51
+ def model_contents
52
+ <<-CONTENT
53
+ extend ModelTokenizer
54
+ has_token #:length => 14
55
+ self.primary_key = :token
56
+ CONTENT
57
+ end
58
+
59
+ def model_exists?
60
+ File.exists?(File.join(destination_root, model_path))
61
+ end
62
+
63
+ def migration_path
64
+ @migration_path ||= File.join("db", "migrate")
65
+ end
66
+
67
+ def model_path
68
+ @model_path ||= File.join("app", "models", "#{file_path}.rb")
69
+ end
70
+
71
+ def migration_exists?(table_name)
72
+ Dir.glob("#{File.join(destination_root, migration_path)}/[0-9]*_*.rb").grep(/\d+_add_model_tokenizer_token_to_#{table_name}.rb$/).first
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,14 @@
1
+ class ModelTokenizerCreate<%= table_name.camelize %> < ActiveRecord::Migration
2
+ def change
3
+ create_table(:<%= table_name %>) do |t|
4
+ <% attributes.each do |attribute| -%>
5
+ t.<%= attribute.type %> :<%= attribute.name %>
6
+ <% end -%>
7
+
8
+ <%= migration_data -%>
9
+ t.timestamps
10
+ end
11
+
12
+ add_index :<%= table_name %>, :token, unique: true
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ class AddModelTokenizerTokenTo<%= table_name.camelize %> < ActiveRecord::Migration
2
+ def self.up
3
+ change_table(:<%= table_name %>) do |t|
4
+ <% attributes.each do |attribute| -%>
5
+ t.<%= attribute.type %> :<%= attribute.name %>
6
+ <% end -%>
7
+ <%= migration_data -%>
8
+ end
9
+
10
+ add_index :<%= table_name %>, :token, unique: true
11
+ end
12
+
13
+ def self.down
14
+ # Impelement rollback yourself.
15
+ raise ActiveRecord::IrreversibleMigration
16
+ end
17
+ end
@@ -0,0 +1,42 @@
1
+ module ModelTokenizer
2
+ module Base
3
+ CHARSET = %w{
4
+ a b c d e f g h i j k m n o p q r s t u v w x y z
5
+ A B C D E F G H J K L M N P R S T W X Y Z
6
+ 2 3 4 5 6 7 8 9
7
+ - _
8
+ }
9
+
10
+ #Default length is 14 characters. Provides
11
+ #56!/(14!*(56-14)!) = 5,804,731,963,800 unique tokens.
12
+ @@model_tokenizer_token_length = 14
13
+
14
+ def model_tokenizer_token_length
15
+ @@model_tokenizer_token_length
16
+ end
17
+
18
+ def has_token(*attributes)
19
+ options = {
20
+ :length => @@model_tokenizer_token_length
21
+ }.merge!(attributes.last.is_a?(Hash) ? attributes.pop : {})
22
+
23
+ if(!options[:length].is_a?(Integer) || options[:length] < 8)
24
+ options[:length] = @@model_tokenizer_token_length
25
+ end
26
+
27
+ @@model_tokenizer_token_length = options[:length]
28
+
29
+ include InstanceMethods
30
+ end
31
+
32
+ module InstanceMethods
33
+ protected
34
+ def generate_token
35
+ self.token = loop do
36
+ random_token = (0...self.class.model_tokenizer_token_length).map{CHARSET[rand(CHARSET.size)]}.join
37
+ break random_token unless self.class.exists?(:token => random_token)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,3 @@
1
+ module ModelTokenizer
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,16 @@
1
+ require "model_tokenizer/version"
2
+ require "model_tokenizer/base"
3
+
4
+ module ModelTokenizer
5
+ def self.extended(base)
6
+ return if base.respond_to? :model_tokenizer
7
+ base.class_eval do
8
+ extend Base
9
+ before_create :generate_token
10
+ end
11
+ end
12
+
13
+ def self.included(base)
14
+ base.extend self
15
+ end
16
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'model_tokenizer/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "model_tokenizer"
8
+ spec.version = ModelTokenizer::VERSION
9
+ spec.authors = ["Adib Saad"]
10
+ spec.email = ["adib.saad@gmail.com"]
11
+ spec.summary = %q{Random token generator for Rails models}
12
+ spec.homepage = "http://rubygems.org/gems/model_tokenizer"
13
+ spec.license = "MIT"
14
+ spec.description =
15
+ %q{ModelTokenizer creates random tokens to be
16
+ used as primary keys for ActiveRecord objects}
17
+
18
+ spec.files = `git ls-files`.split("\n")
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.7"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "activerecord", "~> 4.0"
26
+ spec.add_development_dependency "sqlite3"
27
+ spec.add_development_dependency "minitest"
28
+ end
@@ -0,0 +1,4 @@
1
+ sqlite3:
2
+ adapter: sqlite3
3
+ database: ":memory:"
4
+ encoding: utf8
data/test/helper.rb ADDED
@@ -0,0 +1,101 @@
1
+ require "bundler/setup"
2
+ # if ENV['COVERAGE']
3
+ # require 'coveralls'
4
+ # Coveralls.wear!
5
+ # end
6
+
7
+ require "active_record"
8
+
9
+ #Put all your 'at_exit's that you want executed after
10
+ #running your tests BEFORE requiring minitest,
11
+ #as minitest uses at_exit to run the tests.
12
+ #Putting the follow line after requiring minitest will
13
+ #close the database connection before the first test runs.
14
+ at_exit {ActiveRecord::Base.connection.disconnect!}
15
+
16
+ require "minitest/autorun"
17
+
18
+ Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
19
+
20
+ #require "mocha/setup"
21
+
22
+ # if ENV["COVERAGE"]
23
+ # require 'simplecov'
24
+ # SimpleCov.start do
25
+ # add_filter "test/"
26
+ # add_filter "friendly_id/migration"
27
+ # end
28
+ # end
29
+
30
+ # I18n.enforce_available_locales = false
31
+
32
+ require 'model_tokenizer'
33
+
34
+ # If you want to see the ActiveRecord log, invoke the tests using `rake test LOG=true`
35
+ # if ENV["LOG"]
36
+ # require "logger"
37
+ # ActiveRecord::Base.logger = Logger.new($stdout)
38
+ # end
39
+
40
+ module ModelTokenizer
41
+ module Test
42
+ def self.included(base)
43
+ MiniTest::Unit.autorun
44
+ end
45
+
46
+ def transaction
47
+ ActiveRecord::Base.transaction { yield ; raise ActiveRecord::Rollback }
48
+ end
49
+
50
+ def with_instance_of(*args)
51
+ model_class = args.shift
52
+ args[0] ||= {:data => "some data goes here"}
53
+ transaction { yield model_class.create!(*args) }
54
+ end
55
+
56
+ module Database
57
+ extend self
58
+
59
+ def connect
60
+ version = ActiveRecord::VERSION::STRING
61
+ driver = ModelTokenizer::Test::Database.driver
62
+ engine = RUBY_ENGINE rescue "ruby"
63
+
64
+ ActiveRecord::Base.establish_connection config[driver]
65
+
66
+ message = "Using #{engine} #{RUBY_VERSION} AR #{version} with #{driver}"
67
+
68
+ puts "-" * 72
69
+ if in_memory?
70
+ ActiveRecord::Migration.verbose = false
71
+ Schema.migrate :up
72
+ puts "#{message} (in-memory)"
73
+ else
74
+ puts message
75
+ end
76
+
77
+ end
78
+
79
+ def config
80
+ @config ||= YAML::load(File.open(File.expand_path("../databases.yml", __FILE__)))
81
+ end
82
+
83
+ def driver
84
+ (ENV["DB"] or "sqlite3").downcase
85
+ end
86
+
87
+ def in_memory?
88
+ config[driver]["database"] == ":memory:"
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ class Module
95
+ def test(name, &block)
96
+ define_method("test_#{name.gsub(/[^a-z0-9']/i, "_")}".to_sym, &block)
97
+ end
98
+ end
99
+
100
+ require "schema"
101
+ ModelTokenizer::Test::Database.connect
data/test/schema.rb ADDED
@@ -0,0 +1,28 @@
1
+ module ModelTokenizer
2
+ module Test
3
+ class Schema < ActiveRecord::Migration
4
+ class << self
5
+ def down
6
+ drop_table :cars
7
+ end
8
+
9
+ def up
10
+ # TODO: use schema version to avoid ugly hacks like this
11
+ return if @done
12
+
13
+ create_table :cars do |t|
14
+ t.string :data
15
+ t.string :token
16
+ end
17
+
18
+ create_table :trucks do |t|
19
+ t.string :data
20
+ t.string :token
21
+ end
22
+
23
+ @done = true
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,42 @@
1
+ require "helper"
2
+
3
+ class Car < ActiveRecord::Base
4
+ extend ModelTokenizer
5
+ has_token
6
+ end
7
+
8
+ class Truck < ActiveRecord::Base
9
+ extend ModelTokenizer
10
+ has_token :length => 16
11
+ end
12
+
13
+ class TokenGenerator < MiniTest::Test
14
+ include ModelTokenizer::Test
15
+
16
+ def setup
17
+ Car.all.each(&:destroy)
18
+ Truck.all.each(&:destroy)
19
+ end
20
+
21
+ def test_that_tokens_are_created_for_models
22
+ with_instance_of(Car) do |record|
23
+ assert record.token, "Token is nil"
24
+ assert record.token.length == Car::model_tokenizer_token_length,
25
+ "Token length is not #{Car::model_tokenizer_token_length}"
26
+
27
+ record.token.split("").each do |c|
28
+ assert ModelTokenizer::Base::CHARSET.include?(c), "#{c} doesn't belong in the acceptable character set"
29
+ end
30
+ end
31
+
32
+ with_instance_of(Truck) do |record|
33
+ assert record.token, "Token is nil"
34
+ assert record.token.length == Truck::model_tokenizer_token_length,
35
+ "Token length is not #{Truck::model_tokenizer_token_length}"
36
+
37
+ record.token.split("").each do |c|
38
+ assert ModelTokenizer::Base::CHARSET.include?(c), "#{c} doesn't belong in the acceptable character set"
39
+ end
40
+ end
41
+ end
42
+ end
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: model_tokenizer
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Adib Saad
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activerecord
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '4.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '4.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sqlite3
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: |-
84
+ ModelTokenizer creates random tokens to be
85
+ used as primary keys for ActiveRecord objects
86
+ email:
87
+ - adib.saad@gmail.com
88
+ executables: []
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - ".gitignore"
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - lib/generators/model_tokenizer/model_tokenizer_generator.rb
98
+ - lib/generators/model_tokenizer/templates/migration.rb
99
+ - lib/generators/model_tokenizer/templates/migration_existing.rb
100
+ - lib/model_tokenizer.rb
101
+ - lib/model_tokenizer/base.rb
102
+ - lib/model_tokenizer/version.rb
103
+ - model_tokenizer.gemspec
104
+ - test/databases.yml
105
+ - test/helper.rb
106
+ - test/schema.rb
107
+ - test/token_generator_test.rb
108
+ homepage: http://rubygems.org/gems/model_tokenizer
109
+ licenses:
110
+ - MIT
111
+ metadata: {}
112
+ post_install_message:
113
+ rdoc_options: []
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ required_rubygems_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ requirements: []
127
+ rubyforge_project:
128
+ rubygems_version: 2.2.2
129
+ signing_key:
130
+ specification_version: 4
131
+ summary: Random token generator for Rails models
132
+ test_files:
133
+ - test/databases.yml
134
+ - test/helper.rb
135
+ - test/schema.rb
136
+ - test/token_generator_test.rb
137
+ has_rdoc: