speaking_id 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -1,10 +1,22 @@
1
1
  = SpeakingId
2
2
 
3
- Creates simple and secure slugs from a given string or random slugs to hide a model ID.
3
+ Lightweight Ruby on Rails gem to handle slugs for Active Record objects.
4
4
 
5
5
  == Installation
6
6
 
7
- You can set it up as a gem in your environment.rb file.
7
+ === Rails 3
8
+
9
+ Add the gem to your project's Gemfile as follows:
10
+
11
+ gem 'speaking_id'
12
+
13
+ Then install the gem by running:
14
+
15
+ bundle install
16
+
17
+ === Rails 2
18
+
19
+ In Rails 2 you can set it up in your environment.rb file.
8
20
 
9
21
  config.gem "speaking_id"
10
22
 
@@ -20,33 +32,33 @@ Alternatively you can install it as a Rails plugin.
20
32
 
21
33
  In your models:
22
34
 
23
- class User < ActiveRecord::Base
24
- has_slug :name
25
- end
26
-
27
- class Project < ActiveRecord::Base
28
- has_random_slug
29
- end
35
+ class User < ActiveRecord::Base
36
+ has_slug :name
37
+ end
38
+
39
+ class Project < ActiveRecord::Base
40
+ has_random_slug
41
+ end
30
42
 
31
43
  In your migrations:
32
44
 
33
- class AddSpeakingIdToUserAndProject < ActiveRecord::Migration
34
- def self.up
35
- add_column :users, :slug, :string
36
- add_column :project, :slug, :string
37
-
38
- add_index :users, :slug, :unique => true
39
- add_index :project, :slug, :unique => true
40
- end
41
-
42
- def self.down
43
- remove_column :users, :slug
44
- remove_column :project, :slug
45
- end
46
- end
45
+ class AddSpeakingIdToUserAndProject < ActiveRecord::Migration
46
+ def self.up
47
+ add_column :users, :slug, :string
48
+ add_column :project, :slug, :string
49
+
50
+ add_index :users, :slug, :unique => true
51
+ add_index :project, :slug, :unique => true
52
+ end
53
+
54
+ def self.down
55
+ remove_column :users, :slug
56
+ remove_column :project, :slug
57
+ end
58
+ end
47
59
 
48
60
  By default, SpeakingId uses a column called "slug". If you want to use a different column name, just pass the +:column+ option:
49
61
 
50
- has_slug :title, :column => :column_name
62
+ has_slug :title, :column => :column_name
51
63
 
52
64
  This works also with the +has_random_slug+ method.
data/Rakefile CHANGED
@@ -1,11 +1,10 @@
1
- require 'rake'
2
1
  require 'rake/testtask'
3
2
 
3
+ desc 'Default: run unit tests.'
4
+ task :default => :test
5
+
6
+ desc 'Test the speaking_id plugin.'
4
7
  Rake::TestTask.new(:test) do |t|
5
- t.libs << 'lib'
6
8
  t.libs << 'test'
7
9
  t.pattern = 'test/**/*_test.rb'
8
- t.verbose = true
9
10
  end
10
-
11
- task :default => :test
@@ -0,0 +1,30 @@
1
+ module SpeakingId
2
+ ASCII_APPROXIMATIONS = {
3
+ 196 => [65, 101], # Ä => Ae
4
+ 214 => [79, 101], # Ö => Oe
5
+ 220 => [85, 101], # Ü => Ue
6
+ 223 => [115, 115], # ß => ss
7
+ 228 => [97, 101], # ä => ae
8
+ 246 => [111, 101], # ö => oe
9
+ 252 => [117, 101] # ü => ue
10
+ }.freeze
11
+
12
+ class << self
13
+ def replace_known_special_chars(chars)
14
+ chars = ActiveSupport::Multibyte.proxy_class.new(chars)
15
+ chars = chars.normalize(:kc).unpack('U*')
16
+
17
+ chars = chars.inject([]) do |result, char|
18
+ result << approximate_char(char)
19
+ end.flatten.pack('U*')
20
+ end
21
+
22
+ def approximate_char(char)
23
+ if ASCII_APPROXIMATIONS.include? char
24
+ return ASCII_APPROXIMATIONS[char]
25
+ end
26
+ char
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,24 @@
1
+ String.class_eval do
2
+ # Replaces known special characters and removes unknown characters in a string
3
+ # so that it may be used as part of a 'pretty' URL.
4
+ def normalize(sep = '-')
5
+ return if self.blank?
6
+
7
+ s = SpeakingId::replace_known_special_chars(self)
8
+
9
+ # Remove anything non-ASCII entirely.
10
+ s = s.gsub(/[^\x00-\x7F]+/, '')
11
+
12
+ # Remove non-word characters.
13
+ s = s.gsub(/[^\w_ \-]+/i, '')
14
+
15
+ # Convert whitespaces to separator.
16
+ s = s.gsub(/[ \-]+/i, sep)
17
+
18
+ # Remove leading/trailing separators.
19
+ s = s.gsub(/^(\_|\-)|(\_|\-)$/i, '')
20
+
21
+ s.downcase
22
+ end
23
+ end
24
+
@@ -1,31 +1,48 @@
1
1
  module SpeakingId
2
- module ClassMethods
3
- def has_slug source, args = {}
4
- include InstanceMethods
2
+ def self.included(base)
3
+ base.send :extend, ClassMethods
4
+ end
5
+
6
+ module ClassMethods
7
+ def has_slug(source, options = {})
8
+ speaking_id(options)
9
+
10
+ class_inheritable_accessor :slug_source
5
11
 
6
- class_inheritable_accessor :slug_source, :slug_column
7
-
8
12
  self.slug_source = source
9
- self.slug_column = args.has_key?(:column) ? args[:column] : :slug
10
13
 
11
14
  before_save :create_slug
12
15
  end
13
16
 
14
- def has_random_slug args = {}
15
- include InstanceMethods
17
+ def has_random_slug(options = {})
18
+ speaking_id(options)
19
+
20
+ before_create :create_random_slug
21
+ end
22
+
23
+ def speaking_id(options = {})
24
+ send :include, InstanceMethods
16
25
 
17
26
  class_inheritable_accessor :slug_column
18
-
19
- self.slug_column = args.has_key?(:column) ? args[:column] : :slug
20
27
 
21
- before_save :create_random_slug
28
+ self.slug_column = (options[:column] || :slug).to_s
29
+
30
+ validates_uniqueness_of self.slug_column
22
31
  end
32
+
33
+ send :protected, :speaking_id
23
34
  end
24
35
 
25
36
  module InstanceMethods
26
37
  def create_slug
38
+ # Only creates a slug when the Active Record object is unsaved or got changed.
39
+ return unless self.instance_eval("#{self.slug_source}_changed?")
40
+
27
41
  begin
28
- self[self.slug_column] = self[self.slug_source].to_s.parameterize("_")
42
+ # Normalizes the slug source column or creates a random slug when blank.
43
+ self[self.slug_column] = self[self.slug_source].normalize
44
+ return create_random_slug if self[self.slug_column].blank?
45
+
29
46
  self[self.slug_column] << ((counter ||= 1) == 1 ? nil : counter).to_s
30
47
  counter += 1
31
48
  end while slug_taken?
@@ -43,12 +60,18 @@ module SpeakingId
43
60
 
44
61
  private
45
62
 
46
- def create_random_token limit = 6
63
+ def create_random_token(limit = 3)
47
64
  ActiveSupport::SecureRandom.hex(limit).upcase
48
65
  end
49
-
66
+
50
67
  def slug_taken?
51
- self.class.first :conditions => {self.slug_column.to_sym => self[self.slug_column]}
68
+ if Rails::VERSION::MAJOR == 2
69
+ self.class.first :conditions => {self.slug_column.to_sym => self[self.slug_column]}
70
+ else
71
+ self.class.where(self.slug_column => self[self.slug_column]).first
72
+ end
52
73
  end
53
74
  end
54
- end
75
+ end
76
+
77
+ ActiveRecord::Base.send :include, SpeakingId
data/lib/speaking_id.rb CHANGED
@@ -1,5 +1,4 @@
1
- require File.dirname(__FILE__) + '/speaking_id/speaking_id'
1
+ require 'speaking_id/ascii_approximations'
2
+ require 'speaking_id/core_extensions'
3
+ require 'speaking_id/speaking_id'
2
4
 
3
- if defined?(ActiveRecord)
4
- ActiveRecord::Base.instance_eval {extend SpeakingId::ClassMethods}
5
- end
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'speaking_id'
@@ -0,0 +1,22 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'test_helper'
4
+
5
+ class SpeakingIdTest < ActiveSupport::TestCase
6
+ test 'should normalize a simple string' do
7
+ assert_equal 'the-sandals', 'The Sandals'.normalize
8
+ end
9
+
10
+ test 'should normalize special characters' do
11
+ assert_equal 'oeaeue-oeaeue-ss', ' ÖÄÜ öäü ß %@: .&? ---'.normalize
12
+ end
13
+
14
+ test 'should normalize a string using underscores as separator' do
15
+ assert_equal 'a_b_c', 'A B C ...'.normalize('_')
16
+ end
17
+
18
+ test 'should normalize and return an empty string' do
19
+ assert ' &?=... '.normalize.empty?
20
+ end
21
+ end
22
+
data/test/schema.rb ADDED
@@ -0,0 +1,15 @@
1
+ ActiveRecord::Schema.define(:version => 0) do
2
+ create_table :articles, :force => true do |t|
3
+ t.string :title
4
+ t.string :slug
5
+ t.text :text
6
+ t.timestamps
7
+ end
8
+
9
+ create_table :users, :force => true do |t|
10
+ t.string :email
11
+ t.string :random_slug
12
+ t.timestamps
13
+ end
14
+ end
15
+
@@ -1,8 +1,54 @@
1
+ # encoding: UTF-8
2
+
1
3
  require 'test_helper'
2
4
 
5
+ class Article < ActiveRecord::Base
6
+ has_slug :title
7
+ end
8
+
9
+ class User < ActiveRecord::Base
10
+ has_random_slug :column => :random_slug
11
+ end
12
+
3
13
  class SpeakingIdTest < ActiveSupport::TestCase
4
- # Replace this with your real tests.
5
- test "the truth" do
6
- assert true
14
+ load_schema
15
+
16
+ def setup
17
+ @article = Article.find_or_create_by_title('The Sandals')
7
18
  end
8
- end
19
+
20
+ test 'should not change the slug' do
21
+ assert_equal 'the-sandals', @article.slug
22
+ @article.text = 'The Sandals make great music.'
23
+ @article.save
24
+ assert_equal 'the-sandals', @article.slug
25
+ end
26
+
27
+ test 'should change the slug' do
28
+ assert_equal 'the-sandals', @article.slug
29
+ @article.title = 'Nico & Rushad'
30
+ @article.save
31
+ assert_equal 'nico-rushad', @article.slug
32
+ end
33
+
34
+ test 'should increase the slug' do
35
+ article = Article.create(:title => 'The Sandals')
36
+ assert_equal 'the-sandals2', article.slug
37
+ end
38
+
39
+ test 'should increase the slug another time' do
40
+ article = Article.create(:title => 'The Sandals')
41
+ assert_equal 'the-sandals3', article.slug
42
+ end
43
+
44
+ test 'should generate a random slug' do
45
+ @article.title = ''
46
+ @article.save
47
+ assert_equal 6, @article.slug.length
48
+ end
49
+
50
+ test 'should create an user with a random slug' do
51
+ user = User.create(:email => 'test@example.com')
52
+ assert_equal 6, user.random_slug.length
53
+ end
54
+ end
data/test/test_helper.rb CHANGED
@@ -1,3 +1,19 @@
1
1
  require 'rubygems'
2
+ require 'test/unit'
2
3
  require 'active_support'
3
- require 'active_support/test_case'
4
+ require 'active_record'
5
+ require 'sqlite3'
6
+
7
+ RAILS_ROOT = File.dirname(__FILE__) + '/../../../..'
8
+ require File.expand_path(File.join(RAILS_ROOT, 'config/environment.rb'))
9
+
10
+ def load_schema
11
+ ActiveRecord::Base.establish_connection({
12
+ 'adapter' => 'sqlite3',
13
+ 'database' => File.dirname(__FILE__) + '/speaking_id.sqlite3'
14
+ })
15
+
16
+ load(File.dirname(__FILE__) + '/schema.rb')
17
+ require File.dirname(__FILE__) + '/../rails/init.rb'
18
+ end
19
+
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: speaking_id
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 2
8
+ - 0
9
+ version: 0.2.0
5
10
  platform: ruby
6
11
  authors:
7
12
  - Martin Jagusch
@@ -9,11 +14,11 @@ autorequire:
9
14
  bindir: bin
10
15
  cert_chain: []
11
16
 
12
- date: 2010-01-15 00:00:00 +01:00
17
+ date: 2010-04-06 00:00:00 +02:00
13
18
  default_executable:
14
19
  dependencies: []
15
20
 
16
- description: Creates simple and secure slugs from a given string or random slugs to hide a model ID.
21
+ description: Creates simple and secure slugs from a given string or random slugs.
17
22
  email: m@venlix.net
18
23
  executables: []
19
24
 
@@ -21,18 +26,20 @@ extensions: []
21
26
 
22
27
  extra_rdoc_files:
23
28
  - README.rdoc
24
- - CHANGELOG.rdoc
25
29
  - LICENSE
26
30
  files:
27
- - lib/speaking_id/speaking_id.rb
28
31
  - lib/speaking_id.rb
32
+ - lib/speaking_id/speaking_id.rb
33
+ - lib/speaking_id/ascii_approximations.rb
34
+ - lib/speaking_id/core_extensions.rb
35
+ - rails/init.rb
29
36
  - test/speaking_id_test.rb
37
+ - test/core_extensions_test.rb
38
+ - test/schema.rb
30
39
  - test/test_helper.rb
31
40
  - LICENSE
32
41
  - README.rdoc
33
42
  - Rakefile
34
- - CHANGELOG.rdoc
35
- - init.rb
36
43
  has_rdoc: true
37
44
  homepage: http://github.com/venlix/speaking_id
38
45
  licenses: []
@@ -52,20 +59,23 @@ required_ruby_version: !ruby/object:Gem::Requirement
52
59
  requirements:
53
60
  - - ">="
54
61
  - !ruby/object:Gem::Version
62
+ segments:
63
+ - 0
55
64
  version: "0"
56
- version:
57
65
  required_rubygems_version: !ruby/object:Gem::Requirement
58
66
  requirements:
59
67
  - - ">="
60
68
  - !ruby/object:Gem::Version
69
+ segments:
70
+ - 1
71
+ - 2
61
72
  version: "1.2"
62
- version:
63
73
  requirements: []
64
74
 
65
75
  rubyforge_project:
66
- rubygems_version: 1.3.5
76
+ rubygems_version: 1.3.6
67
77
  signing_key:
68
78
  specification_version: 3
69
- summary: Simple slugs for ActiveRecord models.
79
+ summary: Lightweight Ruby on Rails gem to handle slugs for Active Record objects.
70
80
  test_files: []
71
81
 
data/CHANGELOG.rdoc DELETED
@@ -1,3 +0,0 @@
1
- 0.1.0 (Jan 15, 2010)
2
-
3
- * initial release
data/init.rb DELETED
@@ -1 +0,0 @@
1
- require 'speaking_id'