speaking_id 0.1.0 → 0.2.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 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'