mm-learnup-sluggable 0.3.1

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.
Files changed (5) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +55 -0
  3. data/Rakefile +80 -0
  4. data/lib/mm-sluggable.rb +154 -0
  5. metadata +87 -0
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Richard Livsey, Scott Taylor / Learnup Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,55 @@
1
+ = MongoMapper::Plugins::LearnupSluggable
2
+
3
+ Tiny plugin for MongoMapper to cache a slugged version of a field
4
+
5
+ == Usage
6
+
7
+ Either load it into all models, or individual models:
8
+
9
+ # add to all models
10
+ MongoMapper::Document.plugin(MongoMapper::Plugins::LearnupSluggable)
11
+
12
+ # add to a specific model
13
+ plugin MongoMapper::Plugins::LearnupSluggable
14
+
15
+ Then call sluggable to configure it
16
+
17
+ sluggable :title, :scope => :account_id
18
+
19
+ == Options
20
+
21
+ Available options are:
22
+
23
+ * :scope - scope to a specific field (default - nil)
24
+ * :key - what the slug key is called (default - :slug)
25
+ * :method - what method to call on the field to sluggify it (default - :parameterize)
26
+ * :callback - when to trigger the slugging (default - :before_validation_on_create)
27
+ * :start - what number to start the version numbers (default - 2)
28
+
29
+ Eg.
30
+
31
+ sluggable :title, :scope => :account_id, :key => :title_slug, :method => :to_url, :start => 42
32
+
33
+ This will slug the title to the title_slug key, scoped to the account, will use String#to_url to slug it and start the versions at 42
34
+
35
+ == Versioning
36
+
37
+ If an item with the same slug exists, it will add a version number to the slug.
38
+
39
+ IE assuming we already have an item with the slug of "dave", the slug will be generated as "dave-1"
40
+
41
+ == Note on Patches/Pull Requests
42
+
43
+ * Fork the project.
44
+ * Make your feature addition or bug fix.
45
+ * Add tests for it. This is important so I don't break it in a future version unintentionally.
46
+ * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself in another branch so I can ignore when I pull)
47
+ * Send me a pull request. Bonus points for topic branches.
48
+
49
+ == Install
50
+
51
+ $ gem install mm-sluggable
52
+
53
+ == Copyright
54
+
55
+ See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,80 @@
1
+ require "rubygems"
2
+ require "rubygems/package_task"
3
+ require "rdoc/task"
4
+
5
+ require "rspec"
6
+ require "rspec/core/rake_task"
7
+ RSpec::Core::RakeTask.new do |t|
8
+ t.rspec_opts = %w(--format documentation --colour)
9
+ end
10
+
11
+ task :default => ["spec"]
12
+
13
+ # This builds the actual gem. For details of what all these options
14
+ # mean, and other ones you can add, check the documentation here:
15
+ #
16
+ # http://rubygems.org/read/chapter/20
17
+ #
18
+ spec = Gem::Specification.new do |s|
19
+
20
+ # Change these as appropriate
21
+ s.name = "mm-learnup-sluggable"
22
+ s.version = "0.3.1"
23
+ s.summary = "MongoMapper plugin to cache a slugged version of a field. Originally forked from mm-sluggable."
24
+ s.author = "Scott Taylor"
25
+ s.email = "scott@railsnewbie.com"
26
+ s.homepage = "http://github.com/GoLearnup/mm-sluggable"
27
+
28
+ s.has_rdoc = true
29
+ s.extra_rdoc_files = %w(README.rdoc)
30
+ s.rdoc_options = %w(--main README.rdoc)
31
+
32
+ # Add any extra files to include in the gem
33
+ s.files = %w(LICENSE Rakefile README.rdoc) + Dir.glob("{spec,lib/**/*}")
34
+ s.require_paths = ["lib"]
35
+
36
+ # If you want to depend on other gems, add them here, along with any
37
+ # relevant versions
38
+ # s.add_dependency("some_other_gem", "~> 0.1.0")
39
+
40
+ s.add_dependency("mongo_mapper", ">= 0.9.0")
41
+
42
+ # If your tests use any gems, include them here
43
+ s.add_development_dependency("rspec")
44
+ end
45
+
46
+ # This task actually builds the gem. We also regenerate a static
47
+ # .gemspec file, which is useful if something (i.e. GitHub) will
48
+ # be automatically building a gem for this project. If you're not
49
+ # using GitHub, edit as appropriate.
50
+ #
51
+ # To publish your gem online, install the 'gemcutter' gem; Read more
52
+ # about that here: http://gemcutter.org/pages/gem_docs
53
+ Gem::PackageTask.new(spec) do |pkg|
54
+ pkg.gem_spec = spec
55
+ end
56
+
57
+ desc "Build the gemspec file #{spec.name}.gemspec"
58
+ task :gemspec do
59
+ file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
60
+ File.open(file, "w") {|f| f << spec.to_ruby }
61
+ end
62
+
63
+ # If you don't want to generate the .gemspec file, just remove this line. Reasons
64
+ # why you might want to generate a gemspec:
65
+ # - using bundler with a git source
66
+ # - building the gem without rake (i.e. gem build blah.gemspec)
67
+ # - maybe others?
68
+ task :package => :gemspec
69
+
70
+ # Generate documentation
71
+ RDoc::Task.new do |rd|
72
+ rd.main = "README.rdoc"
73
+ rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
74
+ rd.rdoc_dir = "rdoc"
75
+ end
76
+
77
+ desc 'Clear out RDoc and generated packages'
78
+ task :clean => [:clobber_rdoc, :clobber_package] do
79
+ rm "#{spec.name}.gemspec"
80
+ end
@@ -0,0 +1,154 @@
1
+ require 'mongo_mapper'
2
+
3
+ module MongoMapper
4
+ module Plugins
5
+ module LearnupSluggable
6
+ extend ActiveSupport::Concern
7
+
8
+ class OldSlugException < StandardError
9
+ attr_accessor :object
10
+ attr_accessor :new_slug
11
+ attr_accessor :old_slug
12
+ end
13
+
14
+ module ClassMethods
15
+ def sluggable(to_slug = :title, options = {})
16
+ class_attribute :slug_options
17
+
18
+ self.slug_options = {
19
+ :to_slug => to_slug,
20
+ :key => :slug,
21
+ :method => :parameterize,
22
+ :scope => nil,
23
+ :max_length => 256,
24
+ :start => 2,
25
+ :callback => :before_validation
26
+ }.merge(options)
27
+
28
+ key slug_options[:key], String
29
+ key :old_slugs, Array, :default => []
30
+
31
+ return_value = slug_options[:callback].is_a?(Array) ?
32
+ self.send(slug_options[:callback][0], :set_slug, slug_options[:callback][1]) :
33
+ self.send(slug_options[:callback], :set_slug)
34
+
35
+ slug_key = self.slug_options[:key]
36
+
37
+ define_method :to_param do
38
+ self.send(slug_key).blank? ? self.id.to_s : self.send(slug_key)
39
+ end
40
+
41
+ # TODO: Should these be included? They break specs...
42
+ # define_method :"#{slug_key}=" do |value|
43
+ # v = value.respond_to?(:downcase) ? value.downcase : value
44
+ # super(v)
45
+ # end
46
+ #
47
+ # define_method :"#{slug_key}" do
48
+ # value = super()
49
+ # value.respond_to?(:downcase) ? value.downcase : value
50
+ # end
51
+ #
52
+ # # Silly, custom attribute readers aren't used by form_helpers
53
+ # # Instead, they use "value_before_type_cast", and we can override
54
+ # # the behavior with the following method.
55
+ # # See http://apidock.com/rails/ActionView/Helpers/InstanceTagMethods/ClassMethods/value_before_type_cast
56
+ # define_method :"#{slug_key}_before_type_cast" do
57
+ # value = super()
58
+ # value.respond_to?(:downcase) ? value.downcase : value
59
+ # end
60
+
61
+ metaclass = class << self; self; end
62
+ metaclass.class_eval do
63
+ define_method :find_by_slug do |slug|
64
+ if obj = where(slug_key => slug).first
65
+ obj
66
+ elsif obj = where(:old_slugs => slug).first
67
+ raise old_slug_exception(slug, obj)
68
+ elsif obj = where(slug_key => /^#{Regexp.escape(slug)}$/i).first
69
+ raise old_slug_exception(slug, obj)
70
+ else
71
+ nil
72
+ end
73
+ end
74
+ end
75
+
76
+ before_update do
77
+ if self.send("#{slug_key}_changed?")
78
+ self.old_slugs = self.old_slugs.reject { |slug| slug == self.send(slug_key) }
79
+ self.old_slugs << self.send("#{slug_key}_was")
80
+ end
81
+
82
+ true
83
+ end
84
+
85
+ return_value
86
+ end
87
+ end
88
+
89
+ def set_slug
90
+ klass = self.class
91
+ while klass.respond_to?(:single_collection_parent)
92
+ superclass = klass.single_collection_parent
93
+ if superclass && superclass.respond_to?(:slug_options)
94
+ klass = superclass
95
+ else
96
+ break
97
+ end
98
+ end
99
+
100
+ options = klass.slug_options
101
+
102
+ to_slug = self[options[:to_slug]]
103
+ return if to_slug.blank?
104
+
105
+ the_slug = raw_slug = to_slug.send(options[:method]).to_s[0...options[:max_length]]
106
+
107
+ conds = {}
108
+ conds[options[:key]] = the_slug
109
+ conds[options[:scope]] = self.send(options[:scope]) if options[:scope]
110
+ conds['_id'] = {
111
+ '$ne' => self.id
112
+ }
113
+
114
+ # todo - remove the loop and use regex instead so we can do it in one query
115
+ i = options[:start]
116
+
117
+ while klass.first(conds)
118
+ conds[options[:key]] = the_slug = "#{raw_slug}-#{i}"
119
+ i += 1
120
+ end
121
+
122
+ self.send(:"#{options[:key]}=", the_slug)
123
+ end
124
+ end
125
+
126
+ def old_slug_exception(slug, obj)
127
+ error = MongoMapper::Plugins::LearnupSluggable::OldSlugException.new
128
+ error.old_slug = slug
129
+ error.new_slug = obj.slug
130
+ error.object = obj
131
+ error
132
+ end
133
+
134
+ def find_by_slug!(slug)
135
+ if obj = find_by_slug(slug)
136
+ obj
137
+ else
138
+ raise MongoMapper::DocumentNotFound, "Couldn't find #{self} with slug: #{slug}"
139
+ end
140
+ end
141
+
142
+ def find_by_slug_or_id(slug_or_id)
143
+ self.find_by_slug(slug_or_id) || self.find_by_id(slug_or_id)
144
+ end
145
+
146
+ def find_by_slug_or_id!(slug_or_id)
147
+ if obj = find_by_slug_or_id(slug_or_id)
148
+ obj
149
+ else
150
+ raise MongoMapper::DocumentNotFound, "Couldn't find #{self} with slug or id: #{slug_or_id}"
151
+ end
152
+ end
153
+ end
154
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mm-learnup-sluggable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Scott Taylor
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-07-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mongo_mapper
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.9.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.9.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description:
47
+ email: scott@railsnewbie.com
48
+ executables: []
49
+ extensions: []
50
+ extra_rdoc_files:
51
+ - README.rdoc
52
+ files:
53
+ - LICENSE
54
+ - Rakefile
55
+ - README.rdoc
56
+ - lib/mm-sluggable.rb
57
+ homepage: http://github.com/GoLearnup/mm-sluggable
58
+ licenses: []
59
+ post_install_message:
60
+ rdoc_options:
61
+ - --main
62
+ - README.rdoc
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ segments:
72
+ - 0
73
+ hash: -2694983074753819281
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ! '>='
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubyforge_project:
82
+ rubygems_version: 1.8.24
83
+ signing_key:
84
+ specification_version: 3
85
+ summary: MongoMapper plugin to cache a slugged version of a field. Originally forked
86
+ from mm-sluggable.
87
+ test_files: []