sequel_slugging 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: 310373b02b1a7684421da3aa997f6f5d51625dd4
4
+ data.tar.gz: 91042267dcde3d42202c379738eadf4f49348838
5
+ SHA512:
6
+ metadata.gz: a05a42095cafd32ed793514508cb8278ccb74c6f3a5178c674e6afed9f49e8fa10be67d79d3e325c00b285614f263fb9fe61e2be40bd5fc899895a884ff35d4e
7
+ data.tar.gz: e552e856cfe0d4fc2481defafef544c90a23f95dba45868f642a4cbd04021685c559991c9c6a2b1f0703c4228acb98578b3991588b826579e56c9763e25c412b
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
4
+ before_install: gem install bundler -v 1.10.5
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sequel-slugging.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Chris Hanks
4
+ Copyright (c) 2017 Joakim Nylén
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # Sequel::Slugging
2
+
3
+ Slugging is a plugin for Sequel that creates slugs from names or similar with an built-in uuid fix for duplicates.
4
+
5
+ This gem is quite similar to `friendly_id`
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'sequel_slugging', github: 'jnylen/sequel_slugging'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install sequel-slugging
22
+
23
+ ## Usage
24
+
25
+ Please look in the spec folder for examples.
26
+
27
+ Basically it's just `plugin :slugging, source: :name` where name is the column you want to grab the slug from.
28
+
29
+ ## History
30
+
31
+ You can keep a slug in history so you can still fetch it when it has changed.
32
+
33
+ First you need to create a table using this:
34
+ ```ruby
35
+ create_table :slug_history do
36
+ primary_key :id
37
+ text :slug, null: false
38
+ integer :sluggable_id, null: false
39
+ text :sluggable_type, null: false
40
+ timestamptz :created_at, null: false
41
+ end
42
+ ```
43
+
44
+ Then add `history: :slug_history` to your plugin line for slugging.
45
+
46
+ ## Development
47
+
48
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
49
+
50
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
51
+
52
+ ## Contributing
53
+
54
+ Bug reports and pull requests are welcome on GitHub at https://github.com/jnylen/sequel_slugging. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
55
+
56
+
57
+ ## License
58
+
59
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
60
+
61
+ ## Authors
62
+
63
+ Creator: [Chris Hanks](https://github.com/chanks/sequel-slugging)
64
+ Maintainer: [Joakim Nylén](https://github.com/jnylen/sequel_slugging)
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << 'spec' << 'lib'
6
+ t.test_files = FileList['spec/**/*_spec.rb']
7
+ end
8
+
9
+ task :default => :test
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'sequel/slugging'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require 'pry'
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,190 @@
1
+ require 'securerandom'
2
+ require 'set'
3
+ require 'babosa'
4
+
5
+ require 'sequel/plugins/slugging/version'
6
+
7
+ module Sequel
8
+ module Plugins
9
+ module Slugging
10
+ UUID_REGEX = /\A[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}\z/
11
+ INTEGER_REGEX = /\A\d{1,}\z/
12
+
13
+ class Error < StandardError; end
14
+
15
+ class << self
16
+ attr_writer :slugifier, :maximum_length
17
+
18
+ def slugifier
19
+ @slugifier ||= proc do |string|
20
+ string.to_slug.normalize.to_s
21
+ end
22
+ end
23
+
24
+ def maximum_length
25
+ @maximum_length ||= 50
26
+ end
27
+
28
+ attr_reader :reserved_words
29
+
30
+ def reserved_words=(value)
31
+ @reserved_words = Set.new(value)
32
+ end
33
+
34
+ def apply(model, opts = {})
35
+ model.instance_eval do
36
+ plugin :instance_hooks
37
+ end
38
+ end
39
+
40
+ def configure(model, source:, regenerate_slug: nil, history: nil)
41
+ model.instance_eval do
42
+ @slugging_opts = {source: source, regenerate_slug: regenerate_slug, history: history}.freeze
43
+ end
44
+ end
45
+ end
46
+
47
+ module ClassMethods
48
+ attr_reader :slugging_opts
49
+
50
+ Sequel::Plugins.inherited_instance_variables(self, :@slugging_opts => ->(h){h.dup.freeze})
51
+ Sequel::Plugins.def_dataset_methods(self, [:from_slug, :from_slug!])
52
+
53
+ def pk_type
54
+ schema = db_schema[primary_key]
55
+
56
+ if schema[:type] == :integer
57
+ :integer
58
+ elsif schema[:db_type] == 'uuid'.freeze
59
+ :uuid
60
+ else
61
+ raise "The sequel-slugging plugin can't handle this pk type: #{pk_schema.inspect}"
62
+ end
63
+ end
64
+ end
65
+
66
+ module InstanceMethods
67
+ private
68
+
69
+ def before_create
70
+ set_slug
71
+ super
72
+ end
73
+
74
+ def before_update
75
+ if p = self.class.slugging_opts[:regenerate_slug]
76
+ set_slug if instance_exec(&p)
77
+ end
78
+
79
+ super
80
+ end
81
+
82
+ def set_slug
83
+ self.slug = find_available_slug
84
+
85
+ if table = self.class.slugging_opts[:history]
86
+ after_save_hook do
87
+ db[table].insert(slug: slug, sluggable_id: pk, sluggable_type: self.class.to_s, created_at: Time.now)
88
+ end
89
+ end
90
+ end
91
+
92
+ def find_available_slug
93
+ candidates = []
94
+
95
+ Array(self.class.slugging_opts[:source]).each do |method_set|
96
+ candidate = Array(method_set).map{|meth| get_slug_component(meth)}.join(' ')
97
+ candidate = Sequel::Plugins::Slugging.slugifier.call(candidate)
98
+ candidate = candidate.slice(0...Sequel::Plugins::Slugging.maximum_length)
99
+
100
+ return candidate if acceptable_slug?(candidate)
101
+ candidates << candidate
102
+ end
103
+
104
+ candidates.each do |candidate|
105
+ return candidate << '-'.freeze << SecureRandom.uuid if acceptable_string?(candidate)
106
+ end
107
+
108
+ SecureRandom.uuid
109
+ end
110
+
111
+ def get_slug_component(method)
112
+ case component = send(method)
113
+ when NilClass, String then component
114
+ else raise Error, "unexpected slug component: #{component.inspect}"
115
+ end
116
+ end
117
+
118
+ def acceptable_slug?(slug)
119
+ return false unless acceptable_string?(slug)
120
+ reserved = Sequel::Plugins::Slugging.reserved_words
121
+ return false if reserved && reserved.include?(slug)
122
+
123
+ ds =
124
+ if history_table = self.class.slugging_opts[:history]
125
+ ds = db[history_table].where(slug: slug, sluggable_type: self.class.to_s)
126
+ # If the record already exists, don't consider its own slug to be 'taken'.
127
+ ds = ds.exclude(sluggable_id: pk) if pk
128
+ ds
129
+ else
130
+ ds = self.class.dataset.where(slug: slug)
131
+ # If the record already exists, don't consider its own slug to be 'taken'.
132
+ ds = ds.exclude(self.class.primary_key => pk) if pk
133
+ ds
134
+ end
135
+
136
+ ds.empty?
137
+ end
138
+
139
+ def acceptable_string?(string)
140
+ string && string != ''.freeze
141
+ end
142
+ end
143
+
144
+ module DatasetMethods
145
+ def from_slug!(pk_or_slug)
146
+ from_slug(pk_or_slug) || raise(Sequel::NoMatchingRow)
147
+ end
148
+
149
+ def from_slug(pk_or_slug)
150
+ pk = model.primary_key
151
+
152
+ case pk_type = model.pk_type
153
+ when :integer
154
+ case pk_or_slug
155
+ when Integer
156
+ where(pk => pk_or_slug).first
157
+ when String
158
+ if pk_or_slug =~ INTEGER_REGEX
159
+ where(pk => pk_or_slug.to_i).first
160
+ else
161
+ lookup_by_slug(pk_or_slug)
162
+ end
163
+ else
164
+ raise "Argument to Dataset#from_slug needs to be a String or Integer"
165
+ end
166
+ when :uuid
167
+ if record = lookup_by_slug(pk_or_slug)
168
+ record
169
+ elsif pk_or_slug =~ UUID_REGEX
170
+ where(pk => pk_or_slug).first
171
+ end
172
+ else
173
+ raise "Unexpected pk_type: #{pk_type.inspect}"
174
+ end
175
+ end
176
+
177
+ def lookup_by_slug(slug)
178
+ if history = model.slugging_opts[:history]
179
+ m = model
180
+ if pk = m.db[history].where(sluggable_type: m.to_s, slug: slug).get(:sluggable_id)
181
+ where(m.primary_key => pk).first
182
+ end
183
+ else
184
+ where(slug: slug).first
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,7 @@
1
+ module Sequel
2
+ module Plugins
3
+ module Slugging
4
+ VERSION = '0.1.0'.freeze
5
+ end
6
+ end
7
+ 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 'sequel/plugins/slugging/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'sequel_slugging'
8
+ spec.version = Sequel::Plugins::Slugging::VERSION
9
+ spec.authors = ["Chris Hanks", "Joakim Nylén"]
10
+ spec.email = ['christopher.m.hanks@gmail.com', 'me@jnylen.nu']
11
+
12
+ spec.summary = %q{This gem creates human-friendly string.}
13
+ spec.homepage = 'https://github.com/jnylen/sequel-slugging'
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = 'exe'
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.10'
22
+ spec.add_development_dependency 'rake', '~> 10.0'
23
+ spec.add_development_dependency 'minitest', '~> 5.8.1'
24
+ spec.add_development_dependency 'minitest-hooks', '~> 1.2.0'
25
+
26
+ spec.add_dependency 'sequel', '~> 4.0'
27
+ spec.add_dependency 'babosa', '~> 1.0.2'
28
+ end
metadata ADDED
@@ -0,0 +1,142 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sequel_slugging
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Chris Hanks
8
+ - Joakim Nylén
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2017-09-01 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.10'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.10'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '10.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '10.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: minitest
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: 5.8.1
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: 5.8.1
56
+ - !ruby/object:Gem::Dependency
57
+ name: minitest-hooks
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: 1.2.0
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: 1.2.0
70
+ - !ruby/object:Gem::Dependency
71
+ name: sequel
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '4.0'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '4.0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: babosa
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: 1.0.2
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: 1.0.2
98
+ description:
99
+ email:
100
+ - christopher.m.hanks@gmail.com
101
+ - me@jnylen.nu
102
+ executables: []
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - ".gitignore"
107
+ - ".travis.yml"
108
+ - CODE_OF_CONDUCT.md
109
+ - Gemfile
110
+ - LICENSE.txt
111
+ - README.md
112
+ - Rakefile
113
+ - bin/console
114
+ - bin/setup
115
+ - lib/sequel/plugins/slugging.rb
116
+ - lib/sequel/plugins/slugging/version.rb
117
+ - sequel_slugging.gemspec
118
+ homepage: https://github.com/jnylen/sequel-slugging
119
+ licenses:
120
+ - MIT
121
+ metadata: {}
122
+ post_install_message:
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubyforge_project:
138
+ rubygems_version: 2.6.11
139
+ signing_key:
140
+ specification_version: 4
141
+ summary: This gem creates human-friendly string.
142
+ test_files: []