cmassimo-friendly_id 3.0.4.2
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/Changelog.md +277 -0
- data/Contributors.md +39 -0
- data/Guide.md +561 -0
- data/LICENSE +19 -0
- data/README.md +83 -0
- data/Rakefile +66 -0
- data/extras/README.txt +3 -0
- data/extras/bench.rb +36 -0
- data/extras/extras.rb +38 -0
- data/extras/prof.rb +14 -0
- data/extras/template-gem.rb +26 -0
- data/extras/template-plugin.rb +28 -0
- data/generators/friendly_id/friendly_id_generator.rb +30 -0
- data/generators/friendly_id/templates/create_slugs.rb +18 -0
- data/lib/friendly_id.rb +73 -0
- data/lib/friendly_id/active_record.rb +52 -0
- data/lib/friendly_id/active_record_adapter/configuration.rb +67 -0
- data/lib/friendly_id/active_record_adapter/finders.rb +156 -0
- data/lib/friendly_id/active_record_adapter/simple_model.rb +123 -0
- data/lib/friendly_id/active_record_adapter/slug.rb +66 -0
- data/lib/friendly_id/active_record_adapter/slugged_model.rb +238 -0
- data/lib/friendly_id/active_record_adapter/tasks.rb +69 -0
- data/lib/friendly_id/configuration.rb +113 -0
- data/lib/friendly_id/finders.rb +109 -0
- data/lib/friendly_id/railtie.rb +20 -0
- data/lib/friendly_id/sequel.rb +5 -0
- data/lib/friendly_id/slug_string.rb +391 -0
- data/lib/friendly_id/slugged.rb +102 -0
- data/lib/friendly_id/status.rb +35 -0
- data/lib/friendly_id/test.rb +291 -0
- data/lib/friendly_id/version.rb +9 -0
- data/lib/generators/friendly_id_generator.rb +25 -0
- data/lib/tasks/friendly_id.rake +19 -0
- data/rails/init.rb +2 -0
- data/test/active_record_adapter/ar_test_helper.rb +119 -0
- data/test/active_record_adapter/basic_slugged_model_test.rb +14 -0
- data/test/active_record_adapter/cached_slug_test.rb +61 -0
- data/test/active_record_adapter/core.rb +98 -0
- data/test/active_record_adapter/custom_normalizer_test.rb +20 -0
- data/test/active_record_adapter/custom_table_name_test.rb +22 -0
- data/test/active_record_adapter/scoped_model_test.rb +118 -0
- data/test/active_record_adapter/simple_test.rb +76 -0
- data/test/active_record_adapter/slug_test.rb +34 -0
- data/test/active_record_adapter/slugged.rb +30 -0
- data/test/active_record_adapter/slugged_status_test.rb +25 -0
- data/test/active_record_adapter/sti_test.rb +22 -0
- data/test/active_record_adapter/support/database.jdbcsqlite3.yml +2 -0
- data/test/active_record_adapter/support/database.mysql.yml +4 -0
- data/test/active_record_adapter/support/database.postgres.yml +6 -0
- data/test/active_record_adapter/support/database.sqlite3.yml +2 -0
- data/test/active_record_adapter/support/models.rb +87 -0
- data/test/active_record_adapter/tasks_test.rb +82 -0
- data/test/friendly_id_test.rb +55 -0
- data/test/slug_string_test.rb +88 -0
- data/test/test_helper.rb +15 -0
- metadata +168 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
module FriendlyId
|
2
|
+
|
3
|
+
# FriendlyId::Status presents information about the status of the
|
4
|
+
# id that was used to find the model. This class can be useful for figuring
|
5
|
+
# out when to redirect to a new URL.
|
6
|
+
class Status
|
7
|
+
|
8
|
+
# The id or name used as the finder argument
|
9
|
+
attr_accessor :name
|
10
|
+
|
11
|
+
# The found result, if any
|
12
|
+
attr_accessor :record
|
13
|
+
|
14
|
+
def initialize(options={})
|
15
|
+
options.each {|key, value| self.send("#{key}=".to_sym, value)}
|
16
|
+
end
|
17
|
+
|
18
|
+
# Did the find operation use a friendly id?
|
19
|
+
def friendly?
|
20
|
+
!! name
|
21
|
+
end
|
22
|
+
|
23
|
+
# Did the find operation use a numeric id?
|
24
|
+
def numeric?
|
25
|
+
!friendly?
|
26
|
+
end
|
27
|
+
|
28
|
+
# Did the find operation use the best available id?
|
29
|
+
def best?
|
30
|
+
record.friendly_id ? friendly? : true
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,291 @@
|
|
1
|
+
Module.send :include, Module.new {
|
2
|
+
def test(name, &block)
|
3
|
+
define_method("test_#{name.gsub(/[^a-z0-9]/i, "_")}".to_sym, &block)
|
4
|
+
end
|
5
|
+
alias :should :test
|
6
|
+
}
|
7
|
+
|
8
|
+
module FriendlyId
|
9
|
+
module Test
|
10
|
+
|
11
|
+
# Tests for any model that implements FriendlyId. Any test that tests model
|
12
|
+
# features should include this module.
|
13
|
+
module Generic
|
14
|
+
|
15
|
+
def setup
|
16
|
+
klass.send delete_all_method
|
17
|
+
end
|
18
|
+
|
19
|
+
def teardown
|
20
|
+
klass.send delete_all_method
|
21
|
+
end
|
22
|
+
|
23
|
+
def instance
|
24
|
+
raise NotImplementedError
|
25
|
+
end
|
26
|
+
|
27
|
+
def klass
|
28
|
+
raise NotImplementedError
|
29
|
+
end
|
30
|
+
|
31
|
+
def other_class
|
32
|
+
raise NotImplementedError
|
33
|
+
end
|
34
|
+
|
35
|
+
def find_method
|
36
|
+
raise NotImplementedError
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_method
|
40
|
+
raise NotImplementedError
|
41
|
+
end
|
42
|
+
|
43
|
+
def update_method
|
44
|
+
raise NotImplementedError
|
45
|
+
end
|
46
|
+
|
47
|
+
def validation_exceptions
|
48
|
+
return RuntimeError
|
49
|
+
end
|
50
|
+
|
51
|
+
test "models should have a friendly id config" do
|
52
|
+
assert_not_nil klass.friendly_id_config
|
53
|
+
end
|
54
|
+
|
55
|
+
test "instances should have a friendly id by default" do
|
56
|
+
assert_not_nil instance.friendly_id
|
57
|
+
end
|
58
|
+
|
59
|
+
test "instances should have a friendly id status" do
|
60
|
+
assert_not_nil instance.friendly_id_status
|
61
|
+
end
|
62
|
+
|
63
|
+
test "instances should be findable by their friendly id" do
|
64
|
+
assert_equal instance, klass.send(find_method, instance.friendly_id)
|
65
|
+
end
|
66
|
+
|
67
|
+
test "instances should be findable by their numeric id as an integer" do
|
68
|
+
assert_equal instance, klass.send(find_method, instance.id.to_i)
|
69
|
+
end
|
70
|
+
|
71
|
+
test "instances should be findable by their numeric id as a string" do
|
72
|
+
assert_equal instance, klass.send(find_method, instance.id.to_s)
|
73
|
+
end
|
74
|
+
|
75
|
+
test "instances should be findable by a numeric friendly_id" do
|
76
|
+
instance = klass.send(create_method, :name => "206")
|
77
|
+
assert_equal instance, klass.send(find_method, instance.friendly_id)
|
78
|
+
end
|
79
|
+
|
80
|
+
test "creation should raise an error if the friendly_id text is reserved" do
|
81
|
+
assert_raise(*[validation_exceptions].flatten) do
|
82
|
+
klass.send(create_method, :name => "new")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
test "creation should raise an error if the friendly_id text is an empty string" do
|
87
|
+
assert_raise(*[validation_exceptions].flatten) do
|
88
|
+
klass.send(create_method, :name => "")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
test "creation should raise an error if the friendly_id text is a blank string" do
|
93
|
+
assert_raise(*[validation_exceptions].flatten) do
|
94
|
+
klass.send(create_method, :name => " ")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
test "creation should raise an error if the friendly_id text is nil and allow_nil is false" do
|
99
|
+
assert_raise(*[validation_exceptions].flatten) do
|
100
|
+
klass.send(create_method, :name => nil)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
test "creation should succeed if the friendly_id text is nil and allow_nil is true" do
|
105
|
+
klass.friendly_id_config.stubs(:allow_nil?).returns(true)
|
106
|
+
assert klass.send(create_method, :name => nil)
|
107
|
+
end
|
108
|
+
|
109
|
+
test "should allow the same friendly_id across models" do
|
110
|
+
other_instance = other_class.send(create_method, :name => instance.name)
|
111
|
+
assert_equal other_instance.friendly_id, instance.friendly_id
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
module Simple
|
117
|
+
|
118
|
+
test "should allow friendly_id to be nillable if allow_nil is true" do
|
119
|
+
klass.friendly_id_config.stubs(:allow_nil?).returns(true)
|
120
|
+
instance = klass.send(create_method, :name => "hello")
|
121
|
+
assert instance.friendly_id
|
122
|
+
instance.name = nil
|
123
|
+
assert instance.send(save_method)
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
# Tests for any model that implements slugs.
|
129
|
+
module Slugged
|
130
|
+
|
131
|
+
test "should have a slug" do
|
132
|
+
assert_not_nil instance.slug
|
133
|
+
end
|
134
|
+
|
135
|
+
test "should not make a new slug unless the friendly_id method value has changed" do
|
136
|
+
instance.note = instance.note.to_s << " updated"
|
137
|
+
instance.send save_method
|
138
|
+
assert_equal 1, instance.slugs.size
|
139
|
+
end
|
140
|
+
|
141
|
+
test "should make a new slug if the friendly_id method value has changed" do
|
142
|
+
instance.name = "Changed title"
|
143
|
+
instance.send save_method
|
144
|
+
assert_equal 2, instance.slugs.size
|
145
|
+
end
|
146
|
+
|
147
|
+
test "should be able to reuse an old friendly_id without incrementing the sequence" do
|
148
|
+
old_title = instance.name
|
149
|
+
old_friendly_id = instance.friendly_id
|
150
|
+
instance.name = "A changed title"
|
151
|
+
instance.send save_method
|
152
|
+
instance.name = old_title
|
153
|
+
instance.send save_method
|
154
|
+
assert_equal old_friendly_id, instance.friendly_id
|
155
|
+
end
|
156
|
+
|
157
|
+
test "should increment the slug sequence for duplicate friendly ids" do
|
158
|
+
instance2 = klass.send(create_method, :name => instance.name)
|
159
|
+
assert_match(/2\z/, instance2.friendly_id)
|
160
|
+
end
|
161
|
+
|
162
|
+
test "should find instance with a sequenced friendly_id" do
|
163
|
+
instance2 = klass.send(create_method, :name => instance.name)
|
164
|
+
assert_equal instance2, klass.send(find_method, instance2.friendly_id)
|
165
|
+
end
|
166
|
+
|
167
|
+
test "should indicate correct status when found with a sequence" do
|
168
|
+
instance2 = klass.send(create_method, :name => instance.name)
|
169
|
+
instance2 = klass.send(find_method, instance2.friendly_id)
|
170
|
+
assert instance2.friendly_id_status.best?
|
171
|
+
end
|
172
|
+
|
173
|
+
test "should remain findable by previous slugs" do
|
174
|
+
old_friendly_id = instance.friendly_id
|
175
|
+
instance.name = "#{old_friendly_id} updated"
|
176
|
+
instance.send(save_method)
|
177
|
+
assert_not_equal old_friendly_id, instance.friendly_id
|
178
|
+
assert_equal instance, klass.send(find_method, old_friendly_id)
|
179
|
+
end
|
180
|
+
|
181
|
+
test "should not create a slug when allow_nil is true and friendy_id text is blank" do
|
182
|
+
klass.friendly_id_config.stubs(:allow_nil?).returns(true)
|
183
|
+
instance = klass.send(create_method, :name => nil)
|
184
|
+
assert_nil instance.slug
|
185
|
+
end
|
186
|
+
|
187
|
+
test "should not allow friendly_id to be nillable even if allow_nil is true" do
|
188
|
+
klass.friendly_id_config.stubs(:allow_nil?).returns(true)
|
189
|
+
instance = klass.send(create_method, :name => "hello")
|
190
|
+
assert instance.friendly_id
|
191
|
+
instance.name = nil
|
192
|
+
assert_raise(*[validation_exceptions].flatten) do
|
193
|
+
instance.send(save_method)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
198
|
+
|
199
|
+
# Tests for FriendlyId::Status.
|
200
|
+
module Status
|
201
|
+
|
202
|
+
test "should default to not friendly" do
|
203
|
+
assert !status.friendly?
|
204
|
+
end
|
205
|
+
|
206
|
+
test "should default to numeric" do
|
207
|
+
assert status.numeric?
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
211
|
+
|
212
|
+
# Tests for FriendlyId::Status for a model that uses slugs.
|
213
|
+
module SluggedStatus
|
214
|
+
|
215
|
+
test "should be friendly if slug is set" do
|
216
|
+
status.slug = Slug.new
|
217
|
+
assert status.friendly?
|
218
|
+
end
|
219
|
+
|
220
|
+
test "should be friendly if name is set" do
|
221
|
+
status.name = "name"
|
222
|
+
assert status.friendly?
|
223
|
+
end
|
224
|
+
|
225
|
+
test "should be current if current slug is set" do
|
226
|
+
status.slug = instance.slug
|
227
|
+
assert status.current?
|
228
|
+
end
|
229
|
+
|
230
|
+
test "should not be current if non-current slug is set" do
|
231
|
+
status.slug = Slug.new(:sluggable => instance)
|
232
|
+
assert !status.current?
|
233
|
+
end
|
234
|
+
|
235
|
+
test "should be best if it is current" do
|
236
|
+
status.slug = instance.slug
|
237
|
+
assert status.best?
|
238
|
+
end
|
239
|
+
|
240
|
+
test "should be best if it is numeric, but record has no slug" do
|
241
|
+
instance.slugs = []
|
242
|
+
instance.slug = nil
|
243
|
+
assert status.best?
|
244
|
+
end
|
245
|
+
|
246
|
+
[:record, :name].each do |symbol|
|
247
|
+
test "should have #{symbol} after find using friendly_id" do
|
248
|
+
instance2 = klass.find(instance.friendly_id)
|
249
|
+
assert_not_nil instance2.friendly_id_status.send(symbol)
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
def status
|
254
|
+
@status ||= instance.friendly_id_status
|
255
|
+
end
|
256
|
+
|
257
|
+
def klass
|
258
|
+
raise NotImplementedError
|
259
|
+
end
|
260
|
+
|
261
|
+
def instance
|
262
|
+
raise NotImplementedError
|
263
|
+
end
|
264
|
+
|
265
|
+
end
|
266
|
+
|
267
|
+
# Tests for models to ensure that they properly implement using the
|
268
|
+
# +normalize_friendly_id+ method to allow developers to hook into the
|
269
|
+
# slug string generation.
|
270
|
+
module CustomNormalizer
|
271
|
+
|
272
|
+
test "should invoke the custom normalizer" do
|
273
|
+
assert_equal "JOE SCHMOE", klass.send(create_method, :name => "Joe Schmoe").friendly_id
|
274
|
+
end
|
275
|
+
|
276
|
+
test "should respect the max_length option" do
|
277
|
+
klass.friendly_id_config.stubs(:max_length).returns(3)
|
278
|
+
assert_equal "JOE", klass.send(create_method, :name => "Joe Schmoe").friendly_id
|
279
|
+
end
|
280
|
+
|
281
|
+
test "should raise an error if the friendly_id text is reserved" do
|
282
|
+
klass.friendly_id_config.stubs(:reserved_words).returns(["JOE"])
|
283
|
+
assert_raise(*[validation_exceptions].flatten) do
|
284
|
+
klass.send(create_method, :name => "Joe")
|
285
|
+
end
|
286
|
+
|
287
|
+
end
|
288
|
+
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
|
4
|
+
class FriendlyIdGenerator < Rails::Generators::Base
|
5
|
+
|
6
|
+
include Rails::Generators::Migration
|
7
|
+
|
8
|
+
MIGRATIONS_FILE = File.join(File.dirname(__FILE__), "..", "..", "generators", "friendly_id", "templates", "create_slugs.rb")
|
9
|
+
|
10
|
+
class_option :"skip-migration", :type => :boolean, :desc => "Don't generate a migration for the slugs table"
|
11
|
+
|
12
|
+
def copy_files(*args)
|
13
|
+
migration_template MIGRATIONS_FILE, "db/migrate/create_slugs.rb" unless options["skip-migration"]
|
14
|
+
end
|
15
|
+
|
16
|
+
# Taken from ActiveRecord's migration generator
|
17
|
+
def self.next_migration_number(dirname) #:nodoc:
|
18
|
+
if ActiveRecord::Base.timestamped_migrations
|
19
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
20
|
+
else
|
21
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
namespace :friendly_id do
|
2
|
+
desc "Make slugs for a model."
|
3
|
+
task :make_slugs => :environment do
|
4
|
+
FriendlyId::TaskRunner.new.make_slugs do |record|
|
5
|
+
puts "%s(%d): friendly_id set to '%s'" % [record.class.to_s, record.id, record.slug.name] if record.slug
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "Regenereate slugs for a model."
|
10
|
+
task :redo_slugs => :environment do
|
11
|
+
FriendlyId::TaskRunner.new.delete_slugs
|
12
|
+
Rake::Task["friendly_id:make_slugs"].invoke
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Destroy obsolete slugs older than DAYS=45 days."
|
16
|
+
task :remove_old_slugs => :environment do
|
17
|
+
FriendlyId::TaskRunner.new.delete_old_slugs
|
18
|
+
end
|
19
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
require "active_record"
|
4
|
+
require "active_support"
|
5
|
+
|
6
|
+
require File.expand_path("../../../lib/friendly_id/active_record", __FILE__)
|
7
|
+
require File.expand_path("../../../generators/friendly_id/templates/create_slugs", __FILE__)
|
8
|
+
require File.expand_path("../support/models", __FILE__)
|
9
|
+
require File.expand_path('../core', __FILE__)
|
10
|
+
require File.expand_path('../slugged', __FILE__)
|
11
|
+
|
12
|
+
local_db_settings = File.expand_path("../support/database.yml", __FILE__)
|
13
|
+
default_db_settings = File.expand_path("../support/database.sqlite3.yml", __FILE__)
|
14
|
+
|
15
|
+
db_settings = File.exists?(local_db_settings) ? local_db_settings : default_db_settings
|
16
|
+
ActiveRecord::Base.establish_connection(YAML::load(File.open(db_settings)))
|
17
|
+
|
18
|
+
class ActiveRecord::Base
|
19
|
+
def log_protected_attribute_removal(*args) end
|
20
|
+
end
|
21
|
+
|
22
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
23
|
+
ActiveRecord::Base.connection.drop_table(table)
|
24
|
+
end
|
25
|
+
ActiveRecord::Migration.verbose = false
|
26
|
+
CreateSlugs.up
|
27
|
+
CreateSupportModels.up
|
28
|
+
|
29
|
+
# A model that uses the automagically configured "cached_slug" column
|
30
|
+
class District < ActiveRecord::Base
|
31
|
+
has_friendly_id :name, :use_slug => true
|
32
|
+
end
|
33
|
+
|
34
|
+
# A model that specifies a custom cached slug column
|
35
|
+
class City < ActiveRecord::Base
|
36
|
+
has_friendly_id :name, :use_slug => true, :cache_column => "my_slug"
|
37
|
+
end
|
38
|
+
|
39
|
+
# A model with a custom slug text normalizer
|
40
|
+
class Person < ActiveRecord::Base
|
41
|
+
has_friendly_id :name, :use_slug => true
|
42
|
+
|
43
|
+
def normalize_friendly_id(string)
|
44
|
+
string.upcase
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
# A slugged model that uses a scope
|
50
|
+
class Resident < ActiveRecord::Base
|
51
|
+
belongs_to :country
|
52
|
+
has_friendly_id :name, :use_slug => true, :scope => :country
|
53
|
+
end
|
54
|
+
|
55
|
+
# A slugged model used as a scope
|
56
|
+
class Country < ActiveRecord::Base
|
57
|
+
has_many :people
|
58
|
+
has_many :residents
|
59
|
+
has_friendly_id :name, :use_slug => true
|
60
|
+
end
|
61
|
+
|
62
|
+
# A model that doesn"t use slugs
|
63
|
+
class User < ActiveRecord::Base
|
64
|
+
has_friendly_id :name
|
65
|
+
has_many :houses
|
66
|
+
end
|
67
|
+
|
68
|
+
# Another model that doesn"t use slugs
|
69
|
+
class Author < ActiveRecord::Base
|
70
|
+
has_friendly_id :name
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
# A model that uses a non-slugged model for its scope
|
75
|
+
class House < ActiveRecord::Base
|
76
|
+
belongs_to :user
|
77
|
+
has_friendly_id :name, :use_slug => true, :scope => :user
|
78
|
+
end
|
79
|
+
|
80
|
+
# A model that uses default slug settings and has a named scope
|
81
|
+
class Post < ActiveRecord::Base
|
82
|
+
has_friendly_id :name, :use_slug => true
|
83
|
+
send FriendlyId::ActiveRecordAdapter::Compat.scope_method, :published, :conditions => { :published => true }
|
84
|
+
end
|
85
|
+
|
86
|
+
# Model that uses a custom table name
|
87
|
+
class Place < ActiveRecord::Base
|
88
|
+
self.table_name = "legacy_table"
|
89
|
+
has_friendly_id :name, :use_slug => true
|
90
|
+
end
|
91
|
+
|
92
|
+
# A model that uses a datetime field for its friendly_id
|
93
|
+
class Event < ActiveRecord::Base
|
94
|
+
has_friendly_id :event_date, :use_slug => true
|
95
|
+
end
|
96
|
+
|
97
|
+
# A base model for single table inheritence
|
98
|
+
class Book < ActiveRecord::Base ; end
|
99
|
+
|
100
|
+
# A model that uses STI
|
101
|
+
class Novel < ::Book
|
102
|
+
has_friendly_id :name, :use_slug => true
|
103
|
+
end
|
104
|
+
|
105
|
+
# A model with no table
|
106
|
+
class Question < ActiveRecord::Base
|
107
|
+
has_friendly_id :name, :use_slug => true
|
108
|
+
end
|
109
|
+
|
110
|
+
# A model to test polymorphic associations
|
111
|
+
class Site < ActiveRecord::Base
|
112
|
+
belongs_to :owner, :polymorphic => true
|
113
|
+
has_friendly_id :name, :use_slug => true
|
114
|
+
end
|
115
|
+
|
116
|
+
# A model used as a polymorphic owner
|
117
|
+
class Company < ActiveRecord::Base
|
118
|
+
has_many :sites, :as => :owner
|
119
|
+
end
|