permalinkable 0.1.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 917d1aae3fcb6fc297bd8eeccff7fbe083ad3271
4
- data.tar.gz: 1ca4b745e27f77cadcf6221885a920c89cc5485b
3
+ metadata.gz: b05ab93469a89d4beee2783d29eb52f973dbbb2b
4
+ data.tar.gz: b28eb5e76a637aee2860c7d93721f889b0e67545
5
5
  SHA512:
6
- metadata.gz: 895dfc0204ac64e66ba324dbc42e24fd2c28fb4d8da52473546e5fa5b4334dcb18df5442599200b174c2d9077cefe2b55ca994f30e3c659ece7880d79f7cd2e7
7
- data.tar.gz: d40972d8bdced2b8ce3164bf0b5b17245f720df4a27c1bc2c4acb3815a2101228542401db06f304407619dbc66b4fc86efcead26b205758ecd2324f52d031edc
6
+ metadata.gz: 1ab5cea1e18446b55fe30646752686a45c5b3b1e8a61ad666d7523d93f010d819828b1576697b8e9d439755e35956ba71d5b58ea9cce8c60a6517c75a37807a2
7
+ data.tar.gz: 1537643b9ea13980eadaaf34108ac4d944d869a2774f70f7b9ace857ddb2fc97553814b62b690e99ab718c3306132e0f626da6b1a9d80c7b570eb307830ff3af
Binary file
data/.gitignore CHANGED
@@ -1,2 +1,2 @@
1
- .DS_Store
1
+ ./**/*/.DS_Store
2
2
  *.gem
@@ -0,0 +1,5 @@
1
+ rvm:
2
+ - 1.9.3
3
+ - 2.0.0
4
+ - 2.1.2
5
+ script: bundle exec rspec
@@ -1,22 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- permalinkable (0.1.0)
4
+ permalinkable (1.0.1)
5
5
  activerecord (~> 4.0)
6
6
  hashie (~> 3.1)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- actionpack (4.1.5)
12
- actionview (= 4.1.5)
13
- activesupport (= 4.1.5)
14
- rack (~> 1.5.2)
15
- rack-test (~> 0.6.2)
16
- actionview (4.1.5)
17
- activesupport (= 4.1.5)
18
- builder (~> 3.1)
19
- erubis (~> 2.7.0)
20
11
  activemodel (4.1.5)
21
12
  activesupport (= 4.1.5)
22
13
  builder (~> 3.1)
@@ -33,25 +24,10 @@ GEM
33
24
  arel (5.0.1.20140414130214)
34
25
  builder (3.2.2)
35
26
  diff-lcs (1.2.5)
36
- erubis (2.7.0)
37
- factory_girl (4.4.0)
38
- activesupport (>= 3.0.0)
39
- factory_girl_rails (4.4.1)
40
- factory_girl (~> 4.4.0)
41
- railties (>= 3.0.0)
42
27
  hashie (3.3.1)
43
28
  i18n (0.6.11)
44
29
  json (1.8.1)
45
30
  minitest (5.4.1)
46
- rack (1.5.2)
47
- rack-test (0.6.2)
48
- rack (>= 1.0)
49
- railties (4.1.5)
50
- actionpack (= 4.1.5)
51
- activesupport (= 4.1.5)
52
- rake (>= 0.8.7)
53
- thor (>= 0.18.1, < 2.0)
54
- rake (10.3.2)
55
31
  rspec (3.0.0)
56
32
  rspec-core (~> 3.0.0)
57
33
  rspec-expectations (~> 3.0.0)
@@ -65,7 +41,6 @@ GEM
65
41
  rspec-support (~> 3.0.0)
66
42
  rspec-support (3.0.4)
67
43
  sqlite3 (1.3.9)
68
- thor (0.19.1)
69
44
  thread_safe (0.3.4)
70
45
  tzinfo (1.2.2)
71
46
  thread_safe (~> 0.1)
@@ -74,8 +49,6 @@ PLATFORMS
74
49
  ruby
75
50
 
76
51
  DEPENDENCIES
77
- factory_girl_rails (~> 4.4)
78
52
  permalinkable!
79
- rake (~> 10.1)
80
53
  rspec (~> 3.0)
81
54
  sqlite3 (~> 1.3)
data/README.md CHANGED
@@ -1,3 +1,47 @@
1
+ # Permalinkable, inspired by [permalink](https://github.com/fnando/permalink)
2
+
3
+ [![Code Climate](https://codeclimate.com/github/yangou/permalinkable.png)](https://codeclimate.com/github/yangou/permalinkable)[![Build Status](https://travis-ci.org/yangou/permalinkable.svg)](https://travis-ci.org/yangou/permalinkable)
4
+
5
+ ## Purpose & Note
6
+ This is a gem inspired from another gem called [permalink](https://github.com/fnando/permalink). Since I often want a permalink that provides no way to tell the database ids,
7
+ I finally came up with the idea about encrypting and prepending it to the original permalink.
8
+ The encryption is implemented with a simple FED algorithm. For more information about FPE(Format Preserving Encryption), please consult the [wikipedia](http://en.wikipedia.org/wiki/Format-preserving_encryption) page.
9
+
10
+ The encryption method of current release is a simple "RC4-40" with a configurable key.
11
+ Note that "RC4-40" is NOT a strong encryption algorithm at all, and you SHOULDN'T rely on it to delivery sensitive information. Also, to prevent inconsistency of encryption, and duplication(although the chance is very low), you should keep your key as a secret and never change it in production.
12
+
13
+ The original implementation of generating permalink involves an infite loop to check the uniqueness in database. It's slow, inefficient and most importantly, it still can't prevent race condition in rails. Since we are now using a FPE algorithm based on database id, which is garanteed to be unique from database, we don't need to put ourselves in that inefficient loop any more.
14
+
15
+ #####Finally, in short ,what's the purpose of this gem?
16
+
17
+ It's only a gem that helps hiding your database ids.
18
+
19
+ ## Instalation
20
+
21
+ gem install permalinkable
22
+
23
+ ## Usage
24
+
25
+ Include the `Permalinkable` module into your model. And call `acts_as_permalinkable` with some parameters.
26
+
27
+ ```ruby
28
+ class User < ActiveRecord::Base
29
+ include Permalinkable
30
+ acts_as_permalinkable permalinkable_attribute: :user_name, length: 100, allow_change: true, permalink_field_name: :permalink
31
+ end
32
+ ```
33
+ And don't forget to create the migration for you permalink column.
34
+
35
+ #### Usage on options:
36
+
37
+ options | descriptions
38
+ :----------- | :-----------
39
+ permalinkable_attribute | the attribute that you want your permalink to be based on.
40
+ permalink_field_name | the column name that you want to store your permalink
41
+ length | the maximum length for your permalink
42
+ allow_change | by default it's false so that once a permalink is set, it can never be changed. Setting it to true will allow it to be changed along with the permalinkable_attribute.
43
+
44
+
1
45
  ## License
2
46
 
3
47
  The MIT License (MIT)
@@ -20,4 +64,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
64
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
65
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
66
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
- THE SOFTWARE.
67
+ THE SOFTWARE.
Binary file
@@ -1,2 +1,3 @@
1
- require 'permalinkable/permalinkable'
2
- require 'permalinkable/configure'
1
+ require 'active_record'
2
+ require 'permalinkable/configure'
3
+ require 'permalinkable/permalinkable'
@@ -1,15 +1,17 @@
1
+ require 'hashie'
2
+
1
3
  module Permalinkable
2
4
  class Configure
3
5
  def self.configure
4
- yield configs
6
+ yield self.configs
5
7
  end
6
8
 
7
9
  def self.configs
8
- @@config ||= Hashie::Mash.new
10
+ @configs ||= Hashie::Mash.new
9
11
  end
10
12
 
11
- def self.key
12
- @@configs.key
13
+ def self.secret
14
+ self.configs.secret
13
15
  end
14
16
  end
15
17
  end
@@ -1,3 +1,5 @@
1
+ require 'securerandom'
2
+
1
3
  module Permalinkable
2
4
  def self.included(base)
3
5
  base.extend ClassMethods
@@ -9,15 +11,18 @@ module Permalinkable
9
11
  self.permalink_options = { permalinkable_attribute: :name, permalink_field_name: :permalink,
10
12
  length: 200, allow_change: false }
11
13
  self.permalink_options.update(options) if options.is_a?(Hash)
14
+ self.permalink_options.freeze
12
15
 
13
16
  send :include, InstanceMethods
14
17
  send :after_save, :save_permalink
18
+ send :attr_readonly, permalink_options[:permalink_field_name]
19
+ send :validates, permalink_options[:permalinkable_attribute], presence: true, length: { minimum: 0 }
15
20
  end
16
21
 
17
22
  def permalink_cipher
18
23
  cipher = OpenSSL::Cipher.new('RC4-40')
19
24
  cipher.encrypt
20
- cipher.key = Base64.urlsafe_decode64(Configure.key)
25
+ cipher.key = Base64.urlsafe_decode64(Configure.secret)
21
26
  cipher
22
27
  end
23
28
  end
@@ -38,7 +43,8 @@ module Permalinkable
38
43
  generated_permalink = generate_permalink
39
44
  if self.send(permalink_options[:permalink_field_name]).blank? || \
40
45
  ( permalink_options[:allow_change] && generated_permalink != self.send(permalink_options[:permalink_field_name]) )
41
- self.update_column(permalink_options[:permalink_field_name].to_s, generated_permalink)
46
+ self.class.unscoped.where(self.class.primary_key => self.id).update_all( permalink_options[:permalink_field_name].to_s => generated_permalink )
47
+ raw_write_attribute(permalink_options[:permalink_field_name].to_s, generated_permalink)
42
48
  end
43
49
  end
44
50
 
@@ -1,8 +1,8 @@
1
1
  module Permalinkable
2
2
  module Version
3
- MAJOR = 0
4
- MINOR = 1
5
- PATCH = 0
3
+ MAJOR = 1
4
+ MINOR = 0
5
+ PATCH = 1
6
6
  STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
7
7
  end
8
8
  end
@@ -37,6 +37,4 @@ Gem::Specification.new do |s|
37
37
  s.add_runtime_dependency "hashie", '~> 3.1'
38
38
  s.add_development_dependency "sqlite3", '~> 1.3'
39
39
  s.add_development_dependency "rspec", '~> 3.0'
40
- s.add_development_dependency "rake", '~> 10.1'
41
- s.add_development_dependency "factory_girl_rails", '~> 4.4'
42
40
  end
@@ -1,148 +1,85 @@
1
- # require "spec_helper"
2
-
3
- # describe Permalink::ActiveRecord do
4
- # let(:model) { Post }
5
-
6
- # before do
7
- # model.delete_all
8
- # model.permalink :title
9
- # end
10
-
11
- # it "responds to options" do
12
- # expect(model).to respond_to(:permalink_options)
13
- # end
14
-
15
- # it "uses default options" do
16
- # model.permalink :title
17
- # record = model.create(:title => "Some nice post")
18
- # expect(record.permalink).to eq("some-nice-post")
19
- # end
20
-
21
- # it "uses custom attribute" do
22
- # model.permalink :title, :to => :slug
23
- # record = model.create(:title => "Some nice post")
24
- # expect(record.slug).to eq("some-nice-post")
25
- # end
26
-
27
- # it "sets permalink before_save" do
28
- # record = model.new(:title => "Some nice post")
29
- # expect(record.permalink).to be_nil
30
- # record.valid?
31
- # expect(record.permalink).to eq("some-nice-post")
32
- # end
33
-
34
- # it "creates unique permalinks" do
35
- # model.permalink :title, :unique => true
36
-
37
- # record = model.create(:title => "Some nice post")
38
- # expect(record.permalink).to eq("some-nice-post")
39
-
40
- # record = model.create(:title => "Some nice post")
41
- # expect(record.permalink).to eq("some-nice-post-2")
42
-
43
- # record = model.create(:title => "Some nice post")
44
- # expect(record.permalink).to eq("some-nice-post-3")
45
- # end
46
-
47
- # it "creates unique permalinks based on scope" do
48
- # model.permalink :title, :unique => true, :scope => :user_id
49
-
50
- # user = User.create!
51
- # another_user = User.create!
52
-
53
- # # Create posts for user
54
- # record = model.create(:title => "Some nice post", :user => user)
55
- # expect(record.permalink).to eq("some-nice-post")
56
-
57
- # record = model.create(:title => "Some nice post", :user => user)
58
- # expect(record.permalink).to eq("some-nice-post-2")
59
-
60
- # # Create posts for another user
61
- # record = model.create(:title => "Some nice post", :user => another_user)
62
- # expect(record.permalink).to eq("some-nice-post")
63
-
64
- # record = model.create(:title => "Some nice post", :user => another_user)
65
- # expect(record.permalink).to eq("some-nice-post-2")
66
- # end
67
-
68
- # it "returns param for unique permalink" do
69
- # model.permalink :title, :to_param => :permalink, :unique => true
70
-
71
- # record = model.create(:title => "Ruby")
72
- # expect(record.to_param).to eq("ruby")
73
-
74
- # record = model.create(:title => "Ruby")
75
- # expect(record.to_param).to eq("ruby-2")
76
- # end
77
-
78
- # it "overrides to_param with custom fields" do
79
- # model.permalink :title, :to => :slug, :to_param => [:slug, :id, "page"]
80
-
81
- # record = model.create(:title => "Some nice post")
82
- # expect(record.to_param).to eq("some-nice-post-#{record.id}-page")
83
- # end
84
-
85
- # it "ignores blank attributes from to_param" do
86
- # model.permalink :title, :to_param => [:id, " ", nil, "\t", :permalink]
87
-
88
- # record = model.create(:title => "Some nice post")
89
- # expect(record.to_param).to eq("#{record.id}-some-nice-post")
90
- # end
91
-
92
- # it "sets permalink if permalink is blank" do
93
- # record = model.create(:title => "Some nice post", :permalink => " ")
94
- # expect(record.permalink).to eq("some-nice-post")
95
- # end
96
-
97
- # it "keeps defined permalink" do
98
- # record = model.create(:title => "Some nice post", :permalink => "awesome-post")
99
- # expect(record.permalink).to eq("awesome-post")
100
- # end
101
-
102
- # it "creates unique permalinks for number-ended titles" do
103
- # model.permalink :title, :unique => true
104
-
105
- # record = model.create(:title => "Rails 3")
106
- # expect(record.permalink).to eq("rails-3")
107
-
108
- # record = model.create(:title => "Rails 3")
109
- # expect(record.permalink).to eq("rails-3-2")
110
- # end
111
-
112
- # it "forces permalink" do
113
- # model.permalink :title, :force => true
114
-
115
- # record = model.create(:title => "Some nice post")
116
- # record.update_attributes :title => "Awesome post"
117
-
118
- # expect(record.permalink).to eq("awesome-post")
119
- # end
120
-
121
- # it "forces permalink and keep unique" do
122
- # model.permalink :title, :force => true, :unique => true
123
-
124
- # record = model.create(:title => "Some nice post")
125
-
126
- # record.update_attributes :title => "Awesome post"
127
- # expect(record.permalink).to eq("awesome-post")
128
-
129
- # record = model.create(:title => "Awesome post")
130
- # expect(record.permalink).to eq("awesome-post-2")
131
- # end
132
-
133
- # it "keeps same permalink when another field changes" do
134
- # model.permalink :title, :force => true, :unique => true
135
-
136
- # record = model.create(:title => "Some nice post")
137
- # record.update_attributes :description => "some description"
138
-
139
- # expect(record.permalink).to eq("some-nice-post")
140
- # end
141
-
142
- # it "overrides to_param method" do
143
- # model.permalink :title
144
-
145
- # record = model.create(:title => "Some nice post")
146
- # expect(record.to_param).to eql("#{record.id}-some-nice-post")
147
- # end
148
- # end
1
+ require "spec_helper"
2
+
3
+ describe Permalinkable do
4
+ context "Non-changable permalink" do
5
+ before(:each) do
6
+ User.delete_all
7
+ Entry.delete_all
8
+ end
9
+
10
+ it 'injects permalink_options as class attribute to the model' do
11
+ expect(User.permalink_options).to include({
12
+ :permalinkable_attribute => :user_name,
13
+ :permalink_field_name => :permalink,
14
+ :length => 100,
15
+ :allow_change => false
16
+ })
17
+ end
18
+
19
+ it 'prevents permalink_options from being modified' do
20
+ expect{ User.permalink_options.merge!(test: 1) }.to raise_error(RuntimeError)
21
+ end
22
+
23
+ it 'adds presence and non-empty validation on permalinkable_attribute' do
24
+ expect(User.create.errors.messages).to include({
25
+ user_name: ["can't be blank", "is too short (minimum is 0 characters)"]
26
+ })
27
+
28
+ expect(Entry.create.errors.messages).to include({
29
+ entry_name: ["can't be blank", "is too short (minimum is 0 characters)"]
30
+ })
31
+ end
32
+
33
+ it 'generates permalink for new model' do
34
+ u = User.create(user_name: 'test user')
35
+ expect(u.permalink).not_to be_empty()
36
+
37
+ e = Entry.create(entry_name: 'test entry')
38
+ expect(e.entry_identity).not_to be_empty()
39
+ end
40
+
41
+ it 'generates decrypteable permalink' do
42
+ u = User.create(user_name: 'test user')
43
+ dec = User.permalink_cipher
44
+ dec.decrypt
45
+ expect(
46
+ dec.update(Base64.urlsafe_decode64(u.permalink.gsub("-test-user", ''))).strip
47
+ ).to eq(u.id.to_s)
48
+ end
49
+
50
+ it 'prevents permalink to be manully modified' do
51
+ u = User.create(user_name: 'test user')
52
+ permalink = u.permalink
53
+ u.update_attributes(permalink: '123123') && u.reload
54
+ expect(u.permalink).to eq(permalink)
55
+
56
+ e = Entry.create(entry_name: 'test entry')
57
+ permalink = e.entry_identity
58
+ e.update_attributes(entry_identity: '123123') && e.reload
59
+ expect(e.entry_identity).to eq(permalink)
60
+ end
61
+
62
+ it 'makes to_param to return permalink if available' do
63
+ u = User.create(user_name: 'test user')
64
+ expect(u.permalink).to eq(u.to_param)
65
+
66
+ e = Entry.create(entry_name: 'test entry')
67
+ expect(e.entry_identity).to eq(e.to_param)
68
+ end
69
+ end
70
+
71
+ context "Changable permalink" do
72
+ before(:each) do
73
+ Entry.delete_all
74
+ end
75
+
76
+ it 'changes permalink once permalinkable_attribute changes' do
77
+ e = Entry.create(entry_name: 'test entry')
78
+ old_permalink = e.entry_identity
79
+ e.update_attributes(entry_name: 'changed entry')
80
+ new_permalink = e.entry_identity
81
+ expect(old_permalink).not_to eq(new_permalink)
82
+ expect(old_permalink.first(4)).to eq(new_permalink.first(4))
83
+ end
84
+ end
85
+ end
@@ -1,13 +1,15 @@
1
1
  require "bundler/setup"
2
2
  require "permalinkable"
3
- require 'securerandom'
4
3
 
5
4
  ActiveRecord::Base
6
5
  .establish_connection(adapter: "sqlite3", database: ":memory:")
7
6
 
8
7
  load("support/schema.rb")
9
8
  require "support/user"
9
+ require "support/entry"
10
10
 
11
11
  Permalinkable::Configure.configure do |config|
12
- config.key = SecureRandom.urlsafe_base64(9, false)
13
- end
12
+ config.secret = SecureRandom.urlsafe_base64(9, false)
13
+ end
14
+
15
+ I18n.enforce_available_locales = false
@@ -0,0 +1,4 @@
1
+ class Entry < ActiveRecord::Base
2
+ include Permalinkable
3
+ acts_as_permalinkable permalinkable_attribute: :entry_name, length: 100, allow_change: true, permalink_field_name: :entry_identity
4
+ end
@@ -3,4 +3,9 @@ ActiveRecord::Schema.define(:version => 0) do
3
3
  t.string :user_name
4
4
  t.string :permalink
5
5
  end
6
+
7
+ create_table :entries do |t|
8
+ t.string :entry_name
9
+ t.string :entry_identity
10
+ end
6
11
  end
@@ -1,4 +1,4 @@
1
1
  class User < ActiveRecord::Base
2
2
  include Permalinkable
3
- acts_as_permalinkable permalinkable_attribute: :username, length: 100
3
+ acts_as_permalinkable permalinkable_attribute: :user_name, length: 100
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: permalinkable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yang Ou
@@ -66,34 +66,6 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '3.0'
69
- - !ruby/object:Gem::Dependency
70
- name: rake
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '10.1'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '10.1'
83
- - !ruby/object:Gem::Dependency
84
- name: factory_girl_rails
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: '4.4'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: '4.4'
97
69
  description: |2
98
70
  This is a gem originated from another gem called permalink.
99
71
  Since I often want a permalink that provides no way to tell the database ids,
@@ -122,17 +94,20 @@ files:
122
94
  - ".gitignore"
123
95
  - ".rspec"
124
96
  - ".rvmrc"
97
+ - ".travis.yml"
125
98
  - Gemfile
126
99
  - Gemfile.lock
127
100
  - README.md
128
101
  - lib/.DS_Store
129
102
  - lib/permalinkable.rb
103
+ - lib/permalinkable/.DS_Store
130
104
  - lib/permalinkable/configure.rb
131
105
  - lib/permalinkable/permalinkable.rb
132
106
  - lib/permalinkable/version.rb
133
107
  - permalink.gemspec
134
108
  - spec/permalinkable/permalinkable_spec.rb
135
109
  - spec/spec_helper.rb
110
+ - spec/support/entry.rb
136
111
  - spec/support/schema.rb
137
112
  - spec/support/user.rb
138
113
  homepage: http://rubygems.org/gems/permalinkable
@@ -162,6 +137,7 @@ summary: Generate permalink attributes on ActiveRecord FPE'ed from database ids.
162
137
  test_files:
163
138
  - spec/permalinkable/permalinkable_spec.rb
164
139
  - spec/spec_helper.rb
140
+ - spec/support/entry.rb
165
141
  - spec/support/schema.rb
166
142
  - spec/support/user.rb
167
143
  has_rdoc: