permalinkable 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 917d1aae3fcb6fc297bd8eeccff7fbe083ad3271
4
+ data.tar.gz: 1ca4b745e27f77cadcf6221885a920c89cc5485b
5
+ SHA512:
6
+ metadata.gz: 895dfc0204ac64e66ba324dbc42e24fd2c28fb4d8da52473546e5fa5b4334dcb18df5442599200b174c2d9077cefe2b55ca994f30e3c659ece7880d79f7cd2e7
7
+ data.tar.gz: d40972d8bdced2b8ce3164bf0b5b17245f720df4a27c1bc2c4acb3815a2101228542401db06f304407619dbc66b4fc86efcead26b205758ecd2324f52d031edc
data/.DS_Store ADDED
Binary file
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ .DS_Store
2
+ *.gem
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use ruby-2.1.2@permalinkable --create
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "https://rubygems.org"
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,81 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ permalinkable (0.1.0)
5
+ activerecord (~> 4.0)
6
+ hashie (~> 3.1)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
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
+ activemodel (4.1.5)
21
+ activesupport (= 4.1.5)
22
+ builder (~> 3.1)
23
+ activerecord (4.1.5)
24
+ activemodel (= 4.1.5)
25
+ activesupport (= 4.1.5)
26
+ arel (~> 5.0.0)
27
+ activesupport (4.1.5)
28
+ i18n (~> 0.6, >= 0.6.9)
29
+ json (~> 1.7, >= 1.7.7)
30
+ minitest (~> 5.1)
31
+ thread_safe (~> 0.1)
32
+ tzinfo (~> 1.1)
33
+ arel (5.0.1.20140414130214)
34
+ builder (3.2.2)
35
+ 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
+ hashie (3.3.1)
43
+ i18n (0.6.11)
44
+ json (1.8.1)
45
+ 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
+ rspec (3.0.0)
56
+ rspec-core (~> 3.0.0)
57
+ rspec-expectations (~> 3.0.0)
58
+ rspec-mocks (~> 3.0.0)
59
+ rspec-core (3.0.4)
60
+ rspec-support (~> 3.0.0)
61
+ rspec-expectations (3.0.4)
62
+ diff-lcs (>= 1.2.0, < 2.0)
63
+ rspec-support (~> 3.0.0)
64
+ rspec-mocks (3.0.4)
65
+ rspec-support (~> 3.0.0)
66
+ rspec-support (3.0.4)
67
+ sqlite3 (1.3.9)
68
+ thor (0.19.1)
69
+ thread_safe (0.3.4)
70
+ tzinfo (1.2.2)
71
+ thread_safe (~> 0.1)
72
+
73
+ PLATFORMS
74
+ ruby
75
+
76
+ DEPENDENCIES
77
+ factory_girl_rails (~> 4.4)
78
+ permalinkable!
79
+ rake (~> 10.1)
80
+ rspec (~> 3.0)
81
+ sqlite3 (~> 1.3)
data/README.md ADDED
@@ -0,0 +1,23 @@
1
+ ## License
2
+
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2014 Yang Ou, released under the MIT license
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
data/lib/.DS_Store ADDED
Binary file
@@ -0,0 +1,15 @@
1
+ module Permalinkable
2
+ class Configure
3
+ def self.configure
4
+ yield configs
5
+ end
6
+
7
+ def self.configs
8
+ @@config ||= Hashie::Mash.new
9
+ end
10
+
11
+ def self.key
12
+ @@configs.key
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,49 @@
1
+ module Permalinkable
2
+ def self.included(base)
3
+ base.extend ClassMethods
4
+ end
5
+
6
+ module ClassMethods
7
+ def acts_as_permalinkable(options = {})
8
+ send :cattr_accessor, :permalink_options
9
+ self.permalink_options = { permalinkable_attribute: :name, permalink_field_name: :permalink,
10
+ length: 200, allow_change: false }
11
+ self.permalink_options.update(options) if options.is_a?(Hash)
12
+
13
+ send :include, InstanceMethods
14
+ send :after_save, :save_permalink
15
+ end
16
+
17
+ def permalink_cipher
18
+ cipher = OpenSSL::Cipher.new('RC4-40')
19
+ cipher.encrypt
20
+ cipher.key = Base64.urlsafe_decode64(Configure.key)
21
+ cipher
22
+ end
23
+ end
24
+
25
+ module InstanceMethods
26
+ def to_param
27
+ existing_permalink = send(permalink_options[:permalink_field_name])
28
+ (existing_permalink.present? && existing_permalink) || id.to_s
29
+ end
30
+
31
+ private
32
+ def generate_permalink
33
+ sanitized = self.send(permalink_options[:permalinkable_attribute]).gsub(/[^[:alnum:]]/, ' ').strip.gsub(/\W+/, '-')
34
+ "#{fpe}-#{sanitized}"[0..permalink_options[:length]]
35
+ end
36
+
37
+ def save_permalink
38
+ generated_permalink = generate_permalink
39
+ if self.send(permalink_options[:permalink_field_name]).blank? || \
40
+ ( 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)
42
+ end
43
+ end
44
+
45
+ def fpe
46
+ @fpe ||= Base64.urlsafe_encode64(self.class.permalink_cipher.update('%11s' % self.id))
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,8 @@
1
+ module Permalinkable
2
+ module Version
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ PATCH = 0
6
+ STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
7
+ end
8
+ end
@@ -0,0 +1,2 @@
1
+ require 'permalinkable/permalinkable'
2
+ require 'permalinkable/configure'
data/permalink.gemspec ADDED
@@ -0,0 +1,42 @@
1
+ require "./lib/permalinkable/version"
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "permalinkable"
5
+ s.version = Permalinkable::Version::STRING
6
+ s.platform = Gem::Platform::RUBY
7
+ s.authors = ["Yang Ou"]
8
+ s.email = ["ouyang871223@gmail.com"]
9
+ s.homepage = "http://rubygems.org/gems/permalinkable"
10
+ s.summary = "Generate permalink attributes on ActiveRecord FPE'ed from database ids."
11
+ s.files = `git ls-files`.split("\n")
12
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
13
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
14
+ s.require_paths = ["lib"]
15
+ s.licenses = ['MIT']
16
+ s.description = <<-DESC
17
+ This is a gem originated from another gem called permalink.
18
+ Since I often want a permalink that provides no way to tell the database ids,
19
+ I came up with the idea about encrypting the id and prepending it to the permalink.
20
+ For more information about FPE(Format Preserving Encryption), please consult the wikipedia.
21
+
22
+ The encryption method of current release is simply RC4-40 with a configurable key.
23
+ Note, RC4-40 is not a strong encryption algorithm at all, and you shouldn't rely on it to delivery sensitive
24
+ information. Also, to prevent inconsistance of encryption, and duplication(although the chance is very low)
25
+ you should keep your key as a secret and never change it.
26
+
27
+ The original implementation of generating permalink involves a infite loop to check uniqueness in database.
28
+ It's slow, inefficient and most importantly, it still can't prevent race condition. And since we are using
29
+ a FPE algorithm on the database id, which is garanteed to be unique from database, we don't need to put ourselves
30
+ in that inefficient loop.
31
+
32
+ Finally, what's the purpose of this gem?
33
+ It's only a gem that helps hiding your database ids.
34
+ DESC
35
+
36
+ s.add_runtime_dependency "activerecord", '~> 4.0'
37
+ s.add_runtime_dependency "hashie", '~> 3.1'
38
+ s.add_development_dependency "sqlite3", '~> 1.3'
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
+ end
@@ -0,0 +1,148 @@
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
@@ -0,0 +1,13 @@
1
+ require "bundler/setup"
2
+ require "permalinkable"
3
+ require 'securerandom'
4
+
5
+ ActiveRecord::Base
6
+ .establish_connection(adapter: "sqlite3", database: ":memory:")
7
+
8
+ load("support/schema.rb")
9
+ require "support/user"
10
+
11
+ Permalinkable::Configure.configure do |config|
12
+ config.key = SecureRandom.urlsafe_base64(9, false)
13
+ end
@@ -0,0 +1,6 @@
1
+ ActiveRecord::Schema.define(:version => 0) do
2
+ create_table :users do |t|
3
+ t.string :user_name
4
+ t.string :permalink
5
+ end
6
+ end
@@ -0,0 +1,4 @@
1
+ class User < ActiveRecord::Base
2
+ include Permalinkable
3
+ acts_as_permalinkable permalinkable_attribute: :username, length: 100
4
+ end
metadata ADDED
@@ -0,0 +1,167 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: permalinkable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Yang Ou
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-08-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: hashie
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sqlite3
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
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
+ description: |2
98
+ This is a gem originated from another gem called permalink.
99
+ Since I often want a permalink that provides no way to tell the database ids,
100
+ I came up with the idea about encrypting the id and prepending it to the permalink.
101
+ For more information about FPE(Format Preserving Encryption), please consult the wikipedia.
102
+
103
+ The encryption method of current release is simply RC4-40 with a configurable key.
104
+ Note, RC4-40 is not a strong encryption algorithm at all, and you shouldn't rely on it to delivery sensitive
105
+ information. Also, to prevent inconsistance of encryption, and duplication(although the chance is very low)
106
+ you should keep your key as a secret and never change it.
107
+
108
+ The original implementation of generating permalink involves a infite loop to check uniqueness in database.
109
+ It's slow, inefficient and most importantly, it still can't prevent race condition. And since we are using
110
+ a FPE algorithm on the database id, which is garanteed to be unique from database, we don't need to put ourselves
111
+ in that inefficient loop.
112
+
113
+ Finally, what's the purpose of this gem?
114
+ It's only a gem that helps hiding your database ids.
115
+ email:
116
+ - ouyang871223@gmail.com
117
+ executables: []
118
+ extensions: []
119
+ extra_rdoc_files: []
120
+ files:
121
+ - ".DS_Store"
122
+ - ".gitignore"
123
+ - ".rspec"
124
+ - ".rvmrc"
125
+ - Gemfile
126
+ - Gemfile.lock
127
+ - README.md
128
+ - lib/.DS_Store
129
+ - lib/permalinkable.rb
130
+ - lib/permalinkable/configure.rb
131
+ - lib/permalinkable/permalinkable.rb
132
+ - lib/permalinkable/version.rb
133
+ - permalink.gemspec
134
+ - spec/permalinkable/permalinkable_spec.rb
135
+ - spec/spec_helper.rb
136
+ - spec/support/schema.rb
137
+ - spec/support/user.rb
138
+ homepage: http://rubygems.org/gems/permalinkable
139
+ licenses:
140
+ - MIT
141
+ metadata: {}
142
+ post_install_message:
143
+ rdoc_options: []
144
+ require_paths:
145
+ - lib
146
+ required_ruby_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ required_rubygems_version: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ requirements: []
157
+ rubyforge_project:
158
+ rubygems_version: 2.2.2
159
+ signing_key:
160
+ specification_version: 4
161
+ summary: Generate permalink attributes on ActiveRecord FPE'ed from database ids.
162
+ test_files:
163
+ - spec/permalinkable/permalinkable_spec.rb
164
+ - spec/spec_helper.rb
165
+ - spec/support/schema.rb
166
+ - spec/support/user.rb
167
+ has_rdoc: