ardb 0.28.1 → 0.29.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.
- checksums.yaml +7 -7
- data/.l.yml +8 -0
- data/.rubocop.yml +3 -0
- data/.t.yml +6 -0
- data/Gemfile +21 -8
- data/README.md +252 -3
- data/ardb.gemspec +14 -10
- data/bin/ardb +3 -1
- data/lib/ardb.rb +110 -80
- data/lib/ardb/adapter/base.rb +73 -47
- data/lib/ardb/adapter/mysql.rb +4 -17
- data/lib/ardb/adapter/postgresql.rb +51 -46
- data/lib/ardb/adapter/sqlite.rb +11 -15
- data/lib/ardb/adapter_spy.rb +18 -30
- data/lib/ardb/cli.rb +29 -24
- data/lib/ardb/cli/clirb.rb +19 -17
- data/lib/ardb/cli/commands.rb +308 -129
- data/lib/ardb/db_tests.rb +4 -4
- data/lib/ardb/default_order_by.rb +13 -21
- data/lib/ardb/migration.rb +15 -16
- data/lib/ardb/record_spy.rb +46 -61
- data/lib/ardb/relation_spy.rb +27 -31
- data/lib/ardb/require_autoloaded_active_record_files.rb +174 -58
- data/lib/ardb/test_helpers.rb +13 -14
- data/lib/ardb/use_db_default.rb +10 -19
- data/lib/ardb/version.rb +3 -1
- data/script/determine_autoloaded_active_record_files.rb +31 -24
- data/test/helper.rb +6 -13
- data/test/support/factory.rb +4 -3
- data/test/support/fake_schema.rb +3 -1
- data/test/support/postgresql/migrations/{.gitkeep → .keep} +0 -0
- data/test/support/postgresql/schema.rb +2 -1
- data/test/support/postgresql/setup_test_db.rb +17 -15
- data/test/support/relative_require_test_db_file.rb +1 -0
- data/test/support/require_test_db_file.rb +1 -0
- data/test/system/.keep +0 -0
- data/test/unit/adapter/base_tests.rb +83 -55
- data/test/unit/adapter/mysql_tests.rb +4 -19
- data/test/unit/adapter/postgresql_tests.rb +21 -30
- data/test/unit/adapter/sqlite_tests.rb +5 -11
- data/test/unit/adapter_spy_tests.rb +6 -17
- data/test/unit/ardb_tests.rb +81 -53
- data/test/unit/cli_tests.rb +232 -157
- data/test/unit/db_tests_tests.rb +7 -7
- data/test/unit/default_order_by_tests.rb +21 -20
- data/test/unit/migration_tests.rb +17 -18
- data/test/unit/record_spy_tests.rb +36 -34
- data/test/unit/relation_spy_tests.rb +40 -63
- data/test/unit/test_helpers_tests.rb +7 -15
- data/test/unit/use_db_default_tests.rb +22 -17
- metadata +117 -84
- data/lib/ardb/has_slug.rb +0 -107
- data/lib/ardb/migration_helpers.rb +0 -77
- data/lib/ardb/pg_json.rb +0 -90
- data/test/support/postgresql/pg_json_migrations/20160519133432_create_pg_json_migrate_test.rb +0 -13
- data/test/system/pg_json_tests.rb +0 -85
- data/test/unit/has_slug_tests.rb +0 -341
- data/test/unit/migration_helpers_tests.rb +0 -65
- data/test/unit/pg_json_tests.rb +0 -39
data/lib/ardb/has_slug.rb
DELETED
@@ -1,107 +0,0 @@
|
|
1
|
-
require 'much-plugin'
|
2
|
-
|
3
|
-
module Ardb
|
4
|
-
|
5
|
-
module HasSlug
|
6
|
-
include MuchPlugin
|
7
|
-
|
8
|
-
DEFAULT_ATTRIBUTE = :slug
|
9
|
-
DEFAULT_PREPROCESSOR = :downcase
|
10
|
-
DEFAULT_SEPARATOR = '-'.freeze
|
11
|
-
|
12
|
-
plugin_included do
|
13
|
-
extend ClassMethods
|
14
|
-
include InstanceMethods
|
15
|
-
|
16
|
-
@ardb_has_slug_configs = Hash.new{ |h, k| h[k] = {} }
|
17
|
-
end
|
18
|
-
|
19
|
-
module ClassMethods
|
20
|
-
|
21
|
-
def has_slug(options = nil)
|
22
|
-
options ||= {}
|
23
|
-
raise(ArgumentError, "a source must be provided") unless options[:source]
|
24
|
-
|
25
|
-
attribute = (options[:attribute] || DEFAULT_ATTRIBUTE).to_sym
|
26
|
-
@ardb_has_slug_configs[attribute].merge!({
|
27
|
-
:source_proc => options[:source].to_proc,
|
28
|
-
:preprocessor_proc => (options[:preprocessor] || DEFAULT_PREPROCESSOR).to_proc,
|
29
|
-
:separator => options[:separator] || DEFAULT_SEPARATOR,
|
30
|
-
:allow_underscores => !!options[:allow_underscores]
|
31
|
-
})
|
32
|
-
|
33
|
-
# since the slug isn't written till an after callback we can't always
|
34
|
-
# validate presence of it
|
35
|
-
validates_presence_of(attribute, :on => :update)
|
36
|
-
|
37
|
-
if options[:skip_unique_validation] != true
|
38
|
-
validates_uniqueness_of(attribute, {
|
39
|
-
:case_sensitive => true,
|
40
|
-
:scope => options[:unique_scope]
|
41
|
-
})
|
42
|
-
end
|
43
|
-
|
44
|
-
after_create :ardb_has_slug_generate_slugs
|
45
|
-
after_update :ardb_has_slug_generate_slugs
|
46
|
-
end
|
47
|
-
|
48
|
-
def ardb_has_slug_configs
|
49
|
-
@ardb_has_slug_configs
|
50
|
-
end
|
51
|
-
|
52
|
-
end
|
53
|
-
|
54
|
-
module InstanceMethods
|
55
|
-
|
56
|
-
private
|
57
|
-
|
58
|
-
def reset_slug(attribute = nil)
|
59
|
-
attribute ||= DEFAULT_ATTRIBUTE
|
60
|
-
self.send("#{attribute}=", nil)
|
61
|
-
end
|
62
|
-
|
63
|
-
def ardb_has_slug_generate_slugs
|
64
|
-
self.class.ardb_has_slug_configs.each do |attr_name, config|
|
65
|
-
slug_source = if !self.send(attr_name) || self.send(attr_name).to_s.empty?
|
66
|
-
self.instance_eval(&config[:source_proc])
|
67
|
-
else
|
68
|
-
self.send(attr_name)
|
69
|
-
end
|
70
|
-
|
71
|
-
generated_slug = Slug.new(slug_source, {
|
72
|
-
:preprocessor => config[:preprocessor_proc],
|
73
|
-
:separator => config[:separator],
|
74
|
-
:allow_underscores => config[:allow_underscores]
|
75
|
-
})
|
76
|
-
next if self.send(attr_name) == generated_slug
|
77
|
-
self.send("#{attr_name}=", generated_slug)
|
78
|
-
self.update_column(attr_name, generated_slug)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
end
|
83
|
-
|
84
|
-
module Slug
|
85
|
-
def self.new(string, options = nil)
|
86
|
-
options ||= {}
|
87
|
-
preprocessor = options[:preprocessor]
|
88
|
-
separator = options[:separator]
|
89
|
-
allow_underscores = options[:allow_underscores]
|
90
|
-
regexp_escaped_sep = Regexp.escape(separator)
|
91
|
-
|
92
|
-
slug = preprocessor.call(string.to_s.dup)
|
93
|
-
# Turn unwanted chars into the separator
|
94
|
-
slug.gsub!(/[^\w#{regexp_escaped_sep}]+/, separator)
|
95
|
-
# Turn underscores into the separator, unless allowing
|
96
|
-
slug.gsub!(/_/, separator) unless allow_underscores
|
97
|
-
# No more than one of the separator in a row.
|
98
|
-
slug.gsub!(/#{regexp_escaped_sep}{2,}/, separator)
|
99
|
-
# Remove leading/trailing separator.
|
100
|
-
slug.gsub!(/\A#{regexp_escaped_sep}|#{regexp_escaped_sep}\z/, '')
|
101
|
-
slug
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
end
|
106
|
-
|
107
|
-
end
|
@@ -1,77 +0,0 @@
|
|
1
|
-
require 'ardb'
|
2
|
-
|
3
|
-
module Ardb
|
4
|
-
|
5
|
-
module MigrationHelpers
|
6
|
-
module_function
|
7
|
-
|
8
|
-
def foreign_key(from_table, from_column, to_table, options={})
|
9
|
-
fk = ForeignKey.new(from_table, from_column, to_table, options)
|
10
|
-
execute(fk.add_sql)
|
11
|
-
end
|
12
|
-
|
13
|
-
def drop_foreign_key(*args)
|
14
|
-
from_table, from_column = args[0..1]
|
15
|
-
options = args.last.kind_of?(Hash) ? args.last : {}
|
16
|
-
fk = ForeignKey.new(from_table, from_column, nil, options)
|
17
|
-
execute(fk.drop_sql)
|
18
|
-
end
|
19
|
-
|
20
|
-
def remove_column_with_fk(table, column)
|
21
|
-
drop_foreign_key(table, column)
|
22
|
-
remove_column(table, column)
|
23
|
-
end
|
24
|
-
|
25
|
-
class ForeignKey
|
26
|
-
attr_reader :from_table, :from_column, :to_table, :to_column, :name, :adapter
|
27
|
-
|
28
|
-
def initialize(from_table, from_column, to_table, options=nil)
|
29
|
-
options ||= {}
|
30
|
-
@from_table = from_table.to_s
|
31
|
-
@from_column = from_column.to_s
|
32
|
-
@to_table = to_table.to_s
|
33
|
-
@to_column = (options[:to_column] || 'id').to_s
|
34
|
-
@name = (options[:name] || "fk_#{@from_table}_#{@from_column}").to_s
|
35
|
-
@adapter = Ardb::Adapter.new(Ardb.config)
|
36
|
-
end
|
37
|
-
|
38
|
-
def add_sql
|
39
|
-
apply_data(@adapter.foreign_key_add_sql)
|
40
|
-
end
|
41
|
-
|
42
|
-
def drop_sql
|
43
|
-
apply_data(@adapter.foreign_key_drop_sql)
|
44
|
-
end
|
45
|
-
|
46
|
-
private
|
47
|
-
|
48
|
-
def apply_data(template_sql)
|
49
|
-
template_sql.
|
50
|
-
gsub(':from_table', @from_table).
|
51
|
-
gsub(':from_column', @from_column).
|
52
|
-
gsub(':to_table', @to_table).
|
53
|
-
gsub(':to_column', @to_column).
|
54
|
-
gsub(':name', @name)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
# This file will setup the AR migration command recorder for being able to
|
59
|
-
# change our stuff, require it in an initializer
|
60
|
-
|
61
|
-
module RecorderMixin
|
62
|
-
|
63
|
-
def foreign_key(*args)
|
64
|
-
record(:foreign_key, args)
|
65
|
-
end
|
66
|
-
|
67
|
-
protected
|
68
|
-
|
69
|
-
def invert_foreign_key(args)
|
70
|
-
[ :drop_foreign_key, args ]
|
71
|
-
end
|
72
|
-
|
73
|
-
end
|
74
|
-
|
75
|
-
end
|
76
|
-
|
77
|
-
end
|
data/lib/ardb/pg_json.rb
DELETED
@@ -1,90 +0,0 @@
|
|
1
|
-
require 'active_record'
|
2
|
-
require 'active_support'
|
3
|
-
|
4
|
-
# Allow ActiveRecord to work with PostgreSQL json/jsonb fields, which aren't
|
5
|
-
# supported with ActiveRecord 3.2
|
6
|
-
# https://github.com/romanbsd/activerecord-postgres-json/blob/master/lib/activerecord-postgres-json/activerecord.rb
|
7
|
-
require 'active_record/connection_adapters/postgresql_adapter'
|
8
|
-
|
9
|
-
module ActiveRecord
|
10
|
-
module ConnectionAdapters
|
11
|
-
PostgreSQLAdapter::NATIVE_DATABASE_TYPES[:json] = { :name => 'json' }
|
12
|
-
PostgreSQLAdapter::NATIVE_DATABASE_TYPES[:jsonb] = { :name => 'jsonb' }
|
13
|
-
|
14
|
-
class PostgreSQLColumn < Column
|
15
|
-
# Adds the json type for the column.
|
16
|
-
def simplified_type_with_json(field_type)
|
17
|
-
case field_type
|
18
|
-
when 'json'
|
19
|
-
:json
|
20
|
-
when 'jsonb'
|
21
|
-
:jsonb
|
22
|
-
else
|
23
|
-
simplified_type_without_json(field_type)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
alias_method_chain :simplified_type, :json
|
28
|
-
|
29
|
-
class << self
|
30
|
-
def extract_value_from_default_with_json(default)
|
31
|
-
case default
|
32
|
-
when "'{}'::json", "'{}'::jsonb"
|
33
|
-
'{}'
|
34
|
-
when "'[]'::json", "'[]'::jsonb"
|
35
|
-
'[]'
|
36
|
-
else
|
37
|
-
extract_value_from_default_without_json(default)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
alias_method_chain :extract_value_from_default, :json
|
42
|
-
end
|
43
|
-
|
44
|
-
end
|
45
|
-
|
46
|
-
class TableDefinition
|
47
|
-
# Adds json type for migrations. So you can add columns to a table like:
|
48
|
-
# create_table :people do |t|
|
49
|
-
# ...
|
50
|
-
# t.json :info
|
51
|
-
# ...
|
52
|
-
# end
|
53
|
-
def json(*args)
|
54
|
-
options = args.extract_options!
|
55
|
-
column_names = args
|
56
|
-
column_names.each { |name| column(name, 'json', options) }
|
57
|
-
end
|
58
|
-
|
59
|
-
def jsonb(*args)
|
60
|
-
options = args.extract_options!
|
61
|
-
column_names = args
|
62
|
-
column_names.each { |name| column(name, 'jsonb', options) }
|
63
|
-
end
|
64
|
-
|
65
|
-
end
|
66
|
-
|
67
|
-
class Table
|
68
|
-
# Adds json type for migrations. So you can add columns to a table like:
|
69
|
-
# change_table :people do |t|
|
70
|
-
# ...
|
71
|
-
# t.json :info
|
72
|
-
# ...
|
73
|
-
# end
|
74
|
-
def json(*args)
|
75
|
-
options = args.extract_options!
|
76
|
-
column_names = args
|
77
|
-
column_names.each { |name| column(name, 'json', options) }
|
78
|
-
end
|
79
|
-
|
80
|
-
def jsonb(*args)
|
81
|
-
options = args.extract_options!
|
82
|
-
column_names = args
|
83
|
-
column_names.each { |name| column(name, 'jsonb', options) }
|
84
|
-
end
|
85
|
-
|
86
|
-
end
|
87
|
-
|
88
|
-
end
|
89
|
-
|
90
|
-
end
|
data/test/support/postgresql/pg_json_migrations/20160519133432_create_pg_json_migrate_test.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
require 'ardb/migration_helpers'
|
2
|
-
|
3
|
-
class CreatePgJsonMigrateTest < ActiveRecord::Migration
|
4
|
-
include Ardb::MigrationHelpers
|
5
|
-
|
6
|
-
def change
|
7
|
-
create_table :pg_json_test_records do |t|
|
8
|
-
t.json :json_attribute
|
9
|
-
end
|
10
|
-
add_column :pg_json_test_records, :jsonb_attribute, :jsonb
|
11
|
-
end
|
12
|
-
|
13
|
-
end
|
@@ -1,85 +0,0 @@
|
|
1
|
-
require 'assert'
|
2
|
-
require 'ardb/pg_json'
|
3
|
-
|
4
|
-
require 'json'
|
5
|
-
require 'test/support/postgresql/setup_test_db'
|
6
|
-
|
7
|
-
module Ardb; end
|
8
|
-
module Ardb::PgJson
|
9
|
-
|
10
|
-
class SystemTests < PostgresqlDbTests
|
11
|
-
desc "Ardb postgresql json shim"
|
12
|
-
setup do
|
13
|
-
@ardb_config.migrations_path = 'pg_json_migrations'
|
14
|
-
end
|
15
|
-
|
16
|
-
should "add support for postgresql json columns to migrations" do
|
17
|
-
# this should migrate the db, adding a record that has json/jsonb columns
|
18
|
-
assert_nothing_raised do
|
19
|
-
silence_stdout{ Ardb.adapter.migrate_db }
|
20
|
-
end
|
21
|
-
|
22
|
-
results = ActiveRecord::Base.connection.execute(
|
23
|
-
"SELECT column_name, data_type " \
|
24
|
-
"FROM INFORMATION_SCHEMA.COLUMNS " \
|
25
|
-
"WHERE table_name = 'pg_json_test_records'"
|
26
|
-
).to_a
|
27
|
-
exp = {
|
28
|
-
'column_name' => 'json_attribute',
|
29
|
-
'data_type' => 'json',
|
30
|
-
}
|
31
|
-
assert_includes exp, results
|
32
|
-
exp = {
|
33
|
-
'column_name' => 'jsonb_attribute',
|
34
|
-
'data_type' => 'jsonb'
|
35
|
-
}
|
36
|
-
assert_includes exp, results
|
37
|
-
end
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
|
-
class WithMigratedTableTests < SystemTests
|
42
|
-
setup do
|
43
|
-
silence_stdout{ Ardb.adapter.migrate_db }
|
44
|
-
@record_class = Class.new(ActiveRecord::Base) do
|
45
|
-
self.table_name = 'pg_json_test_records'
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
should "add support for postgresql 'json' attributes on records" do
|
50
|
-
values = [Factory.string, Factory.integer, nil]
|
51
|
-
|
52
|
-
record = @record_class.new
|
53
|
-
assert_nil record.json_attribute
|
54
|
-
assert_nil record.jsonb_attribute
|
55
|
-
|
56
|
-
hash = Factory.integer(3).times.inject({}) do |h, n|
|
57
|
-
h.merge!(Factory.string => values.sample)
|
58
|
-
end
|
59
|
-
record.json_attribute = JSON.dump(hash)
|
60
|
-
record.jsonb_attribute = JSON.dump(hash)
|
61
|
-
assert_nothing_raised{ record.save! }
|
62
|
-
record.reload
|
63
|
-
assert_equal hash, JSON.load(record.json_attribute)
|
64
|
-
assert_equal hash, JSON.load(record.jsonb_attribute)
|
65
|
-
|
66
|
-
array = Factory.integer(3).times.map{ values.sample }
|
67
|
-
record.json_attribute = JSON.dump(array)
|
68
|
-
record.jsonb_attribute = JSON.dump(array)
|
69
|
-
assert_nothing_raised{ record.save! }
|
70
|
-
record.reload
|
71
|
-
assert_equal array, JSON.load(record.json_attribute)
|
72
|
-
assert_equal array, JSON.load(record.jsonb_attribute)
|
73
|
-
|
74
|
-
value = values.sample
|
75
|
-
record.json_attribute = JSON.dump(value)
|
76
|
-
record.jsonb_attribute = JSON.dump(value)
|
77
|
-
assert_nothing_raised{ record.save! }
|
78
|
-
record.reload
|
79
|
-
assert_equal value, JSON.load(record.json_attribute)
|
80
|
-
assert_equal value, JSON.load(record.jsonb_attribute)
|
81
|
-
end
|
82
|
-
|
83
|
-
end
|
84
|
-
|
85
|
-
end
|
data/test/unit/has_slug_tests.rb
DELETED
@@ -1,341 +0,0 @@
|
|
1
|
-
require 'assert'
|
2
|
-
require 'ardb/has_slug'
|
3
|
-
|
4
|
-
require 'much-plugin'
|
5
|
-
require 'ardb/record_spy'
|
6
|
-
|
7
|
-
module Ardb::HasSlug
|
8
|
-
|
9
|
-
class UnitTests < Assert::Context
|
10
|
-
desc "Ardb::HasSlug"
|
11
|
-
setup do
|
12
|
-
source_attribute = @source_attribute = Factory.string.to_sym
|
13
|
-
slug_attribute = @slug_attribute = Factory.string.to_sym
|
14
|
-
@record_class = Ardb::RecordSpy.new do
|
15
|
-
include Ardb::HasSlug
|
16
|
-
attr_accessor source_attribute, slug_attribute, DEFAULT_ATTRIBUTE
|
17
|
-
attr_reader :slug_db_column_updates
|
18
|
-
|
19
|
-
def update_column(*args)
|
20
|
-
@slug_db_column_updates ||= []
|
21
|
-
@slug_db_column_updates << args
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
subject{ @record_class }
|
26
|
-
|
27
|
-
NON_WORD_CHARS = ((' '..'/').to_a + (':'..'@').to_a + ('['..'`').to_a +
|
28
|
-
('{'..'~').to_a - ['-', '_']).freeze
|
29
|
-
|
30
|
-
should have_imeths :has_slug
|
31
|
-
should have_imeths :ardb_has_slug_configs
|
32
|
-
|
33
|
-
should "use much-plugin" do
|
34
|
-
assert_includes MuchPlugin, Ardb::HasSlug
|
35
|
-
end
|
36
|
-
|
37
|
-
should "know its default attribute, preprocessor and separator" do
|
38
|
-
assert_equal :slug, DEFAULT_ATTRIBUTE
|
39
|
-
assert_equal :downcase, DEFAULT_PREPROCESSOR
|
40
|
-
assert_equal '-', DEFAULT_SEPARATOR
|
41
|
-
end
|
42
|
-
|
43
|
-
should "not have any has-slug configs by default" do
|
44
|
-
assert_equal({}, subject.ardb_has_slug_configs)
|
45
|
-
end
|
46
|
-
|
47
|
-
should "default the has slug config using `has_slug`" do
|
48
|
-
subject.has_slug :source => @source_attribute
|
49
|
-
string = Factory.string
|
50
|
-
record = subject.new.tap{ |r| r.send("#{@source_attribute}=", string) }
|
51
|
-
|
52
|
-
config = subject.ardb_has_slug_configs[DEFAULT_ATTRIBUTE]
|
53
|
-
assert_equal DEFAULT_SEPARATOR, config[:separator]
|
54
|
-
assert_false config[:allow_underscores]
|
55
|
-
|
56
|
-
source_proc = config[:source_proc]
|
57
|
-
assert_instance_of Proc, source_proc
|
58
|
-
exp = record.send(@source_attribute)
|
59
|
-
assert_equal exp, record.instance_eval(&source_proc)
|
60
|
-
|
61
|
-
upcase_string = string.upcase
|
62
|
-
preprocessor_proc = config[:preprocessor_proc]
|
63
|
-
assert_instance_of Proc, preprocessor_proc
|
64
|
-
exp = upcase_string.send(DEFAULT_PREPROCESSOR)
|
65
|
-
assert_equal exp, preprocessor_proc.call(upcase_string)
|
66
|
-
end
|
67
|
-
|
68
|
-
should "allow customizing the has slug config using `has_slug`" do
|
69
|
-
separator = NON_WORD_CHARS.sample
|
70
|
-
allow_underscore = Factory.boolean
|
71
|
-
subject.has_slug({
|
72
|
-
:attribute => @slug_attribute,
|
73
|
-
:source => @source_attribute,
|
74
|
-
:preprocessor => :upcase,
|
75
|
-
:separator => separator,
|
76
|
-
:allow_underscores => allow_underscore
|
77
|
-
})
|
78
|
-
|
79
|
-
config = subject.ardb_has_slug_configs[@slug_attribute]
|
80
|
-
assert_equal separator, config[:separator]
|
81
|
-
assert_equal allow_underscore, config[:allow_underscores]
|
82
|
-
|
83
|
-
value = Factory.string.downcase
|
84
|
-
preprocessor_proc = config[:preprocessor_proc]
|
85
|
-
assert_instance_of Proc, preprocessor_proc
|
86
|
-
assert_equal value.upcase, preprocessor_proc.call(value)
|
87
|
-
end
|
88
|
-
|
89
|
-
should "add validations using `has_slug`" do
|
90
|
-
subject.has_slug :source => @source_attribute
|
91
|
-
exp_attr_name = DEFAULT_ATTRIBUTE
|
92
|
-
|
93
|
-
validation = subject.validations.find{ |v| v.type == :presence }
|
94
|
-
assert_not_nil validation
|
95
|
-
assert_equal [exp_attr_name], validation.columns
|
96
|
-
assert_equal :update, validation.options[:on]
|
97
|
-
|
98
|
-
validation = subject.validations.find{ |v| v.type == :uniqueness }
|
99
|
-
assert_not_nil validation
|
100
|
-
assert_equal [exp_attr_name], validation.columns
|
101
|
-
assert_equal true, validation.options[:case_sensitive]
|
102
|
-
assert_nil validation.options[:scope]
|
103
|
-
end
|
104
|
-
|
105
|
-
should "not add a unique validation if skipping unique validation" do
|
106
|
-
subject.has_slug({
|
107
|
-
:source => @source_attribute,
|
108
|
-
:skip_unique_validation => true
|
109
|
-
})
|
110
|
-
|
111
|
-
validation = subject.validations.find{ |v| v.type == :uniqueness }
|
112
|
-
assert_nil validation
|
113
|
-
end
|
114
|
-
|
115
|
-
should "allow customizing its validations using `has_slug`" do
|
116
|
-
unique_scope = Factory.string.to_sym
|
117
|
-
subject.has_slug({
|
118
|
-
:source => @source_attribute,
|
119
|
-
:unique_scope => unique_scope
|
120
|
-
})
|
121
|
-
|
122
|
-
validation = subject.validations.find{ |v| v.type == :uniqueness }
|
123
|
-
assert_not_nil validation
|
124
|
-
assert_equal unique_scope, validation.options[:scope]
|
125
|
-
end
|
126
|
-
|
127
|
-
should "add callbacks using `has_slug`" do
|
128
|
-
subject.has_slug :source => @source_attribute
|
129
|
-
|
130
|
-
callback = subject.callbacks.find{ |v| v.type == :after_create }
|
131
|
-
assert_not_nil callback
|
132
|
-
assert_equal [:ardb_has_slug_generate_slugs], callback.args
|
133
|
-
|
134
|
-
callback = subject.callbacks.find{ |v| v.type == :after_update }
|
135
|
-
assert_not_nil callback
|
136
|
-
assert_equal [:ardb_has_slug_generate_slugs], callback.args
|
137
|
-
end
|
138
|
-
|
139
|
-
should "raise an argument error if `has_slug` isn't passed a source" do
|
140
|
-
assert_raises(ArgumentError){ subject.has_slug }
|
141
|
-
end
|
142
|
-
|
143
|
-
end
|
144
|
-
|
145
|
-
class InitTests < UnitTests
|
146
|
-
desc "when init"
|
147
|
-
setup do
|
148
|
-
@preprocessor = [:downcase, :upcase, :capitalize].sample
|
149
|
-
@separator = NON_WORD_CHARS.sample
|
150
|
-
@allow_underscores = Factory.boolean
|
151
|
-
|
152
|
-
@record_class.has_slug(:source => @source_attribute)
|
153
|
-
@record_class.has_slug({
|
154
|
-
:attribute => @slug_attribute,
|
155
|
-
:source => @source_attribute,
|
156
|
-
:preprocessor => @preprocessor,
|
157
|
-
:separator => @separator,
|
158
|
-
:allow_underscores => @allow_underscores,
|
159
|
-
})
|
160
|
-
|
161
|
-
@record = @record_class.new
|
162
|
-
|
163
|
-
# create a string that has mixed case and an underscore so we can test
|
164
|
-
# that it uses the preprocessor and allow underscores options when
|
165
|
-
# generating a slug
|
166
|
-
@source_value = "#{Factory.string.downcase}_#{Factory.string.upcase}"
|
167
|
-
@record.send("#{@source_attribute}=", @source_value)
|
168
|
-
|
169
|
-
@exp_default_slug = Slug.new(@source_value, {
|
170
|
-
:preprocessor => DEFAULT_PREPROCESSOR.to_proc,
|
171
|
-
:separator => DEFAULT_SEPARATOR
|
172
|
-
})
|
173
|
-
@exp_custom_slug = Slug.new(@source_value, {
|
174
|
-
:preprocessor => @preprocessor.to_proc,
|
175
|
-
:separator => @separator,
|
176
|
-
:allow_underscores => @allow_underscores
|
177
|
-
})
|
178
|
-
end
|
179
|
-
subject{ @record }
|
180
|
-
|
181
|
-
should "reset its slug using `reset_slug`" do
|
182
|
-
# reset the default attribute
|
183
|
-
subject.send("#{DEFAULT_ATTRIBUTE}=", Factory.slug)
|
184
|
-
assert_not_nil subject.send(DEFAULT_ATTRIBUTE)
|
185
|
-
subject.instance_eval{ reset_slug }
|
186
|
-
assert_nil subject.send(DEFAULT_ATTRIBUTE)
|
187
|
-
|
188
|
-
# reset a custom attribute
|
189
|
-
subject.send("#{@slug_attribute}=", Factory.slug)
|
190
|
-
assert_not_nil subject.send(@slug_attribute)
|
191
|
-
sa = @slug_attribute
|
192
|
-
subject.instance_eval{ reset_slug(sa) }
|
193
|
-
assert_nil subject.send(@slug_attribute)
|
194
|
-
end
|
195
|
-
|
196
|
-
should "default its slug attribute" do
|
197
|
-
subject.instance_eval{ ardb_has_slug_generate_slugs }
|
198
|
-
assert_equal 2, subject.slug_db_column_updates.size
|
199
|
-
|
200
|
-
exp = @exp_default_slug
|
201
|
-
assert_equal exp, subject.send(DEFAULT_ATTRIBUTE)
|
202
|
-
assert_includes [DEFAULT_ATTRIBUTE, exp], subject.slug_db_column_updates
|
203
|
-
|
204
|
-
exp = @exp_custom_slug
|
205
|
-
assert_equal exp, subject.send(@slug_attribute)
|
206
|
-
assert_includes [@slug_attribute, exp], subject.slug_db_column_updates
|
207
|
-
end
|
208
|
-
|
209
|
-
should "not set its slug if it hasn't changed" do
|
210
|
-
@record.send("#{DEFAULT_ATTRIBUTE}=", @exp_default_slug)
|
211
|
-
@record.send("#{@slug_attribute}=", @exp_custom_slug)
|
212
|
-
|
213
|
-
subject.instance_eval{ ardb_has_slug_generate_slugs }
|
214
|
-
assert_nil subject.slug_db_column_updates
|
215
|
-
end
|
216
|
-
|
217
|
-
should "slug its slug attribute value if set" do
|
218
|
-
@record.send("#{@slug_attribute}=", @source_value)
|
219
|
-
# change the source attr to some random value, to avoid a false positive
|
220
|
-
@record.send("#{@source_attribute}=", Factory.string)
|
221
|
-
subject.instance_eval{ ardb_has_slug_generate_slugs }
|
222
|
-
|
223
|
-
exp = @exp_custom_slug
|
224
|
-
assert_equal exp, subject.send(@slug_attribute)
|
225
|
-
assert_includes [@slug_attribute, exp], subject.slug_db_column_updates
|
226
|
-
end
|
227
|
-
|
228
|
-
should "slug its source even if its already a valid slug" do
|
229
|
-
slug_source = Factory.slug
|
230
|
-
@record.send("#{@source_attribute}=", slug_source)
|
231
|
-
# ensure the preprocessor doesn't change our source
|
232
|
-
Assert.stub(slug_source, @preprocessor){ slug_source }
|
233
|
-
|
234
|
-
subject.instance_eval{ ardb_has_slug_generate_slugs }
|
235
|
-
|
236
|
-
exp = Slug.new(slug_source, {
|
237
|
-
:preprocessor => @preprocessor.to_proc,
|
238
|
-
:separator => @separator,
|
239
|
-
:allow_underscores => @allow_underscores
|
240
|
-
})
|
241
|
-
assert_equal exp, subject.send(@slug_attribute)
|
242
|
-
assert_includes [@slug_attribute, exp], subject.slug_db_column_updates
|
243
|
-
end
|
244
|
-
|
245
|
-
end
|
246
|
-
|
247
|
-
class SlugTests < UnitTests
|
248
|
-
desc "Slug"
|
249
|
-
setup do
|
250
|
-
@no_op_pp = proc{ |slug| slug }
|
251
|
-
@args = {
|
252
|
-
:preprocessor => @no_op_pp,
|
253
|
-
:separator => '-'
|
254
|
-
}
|
255
|
-
end
|
256
|
-
subject{ Slug }
|
257
|
-
|
258
|
-
should have_imeths :new
|
259
|
-
|
260
|
-
should "always dup the given string" do
|
261
|
-
string = Factory.string
|
262
|
-
assert_not_same string, subject.new(string, @args)
|
263
|
-
end
|
264
|
-
|
265
|
-
should "not change strings that are made up of valid chars" do
|
266
|
-
string = Factory.string
|
267
|
-
assert_equal string, subject.new(string, @args)
|
268
|
-
|
269
|
-
string = "#{Factory.string}-#{Factory.string.upcase}"
|
270
|
-
assert_equal string, subject.new(string, @args)
|
271
|
-
end
|
272
|
-
|
273
|
-
should "turn invalid chars into a separator" do
|
274
|
-
string = Factory.integer(3).times.map do
|
275
|
-
"#{Factory.string(3)}#{NON_WORD_CHARS.sample}#{Factory.string(3)}"
|
276
|
-
end.join(NON_WORD_CHARS.sample)
|
277
|
-
assert_equal string.gsub(/[^\w]+/, '-'), subject.new(string, @args)
|
278
|
-
end
|
279
|
-
|
280
|
-
should "allow passing a custom preprocessor proc" do
|
281
|
-
string = "#{Factory.string}-#{Factory.string.upcase}"
|
282
|
-
exp = string.downcase
|
283
|
-
assert_equal exp, subject.new(string, @args.merge(:preprocessor => :downcase.to_proc))
|
284
|
-
|
285
|
-
preprocessor = proc{ |s| s.gsub(/[A-Z]/, 'a') }
|
286
|
-
exp = preprocessor.call(string)
|
287
|
-
assert_equal exp, subject.new(string, @args.merge(:preprocessor => preprocessor))
|
288
|
-
end
|
289
|
-
|
290
|
-
should "allow passing a custom separator" do
|
291
|
-
separator = NON_WORD_CHARS.sample
|
292
|
-
|
293
|
-
invalid_char = (NON_WORD_CHARS - [separator]).sample
|
294
|
-
string = "#{Factory.string}#{invalid_char}#{Factory.string}"
|
295
|
-
exp = string.gsub(/[^\w]+/, separator)
|
296
|
-
assert_equal exp, subject.new(string, @args.merge(:separator => separator))
|
297
|
-
|
298
|
-
# it won't change the separator in the strings
|
299
|
-
string = "#{Factory.string}#{separator}#{Factory.string}"
|
300
|
-
exp = string
|
301
|
-
assert_equal string, subject.new(string, @args.merge(:separator => separator))
|
302
|
-
|
303
|
-
# it will change the default separator now
|
304
|
-
string = "#{Factory.string}-#{Factory.string}"
|
305
|
-
exp = string.gsub('-', separator)
|
306
|
-
assert_equal exp, subject.new(string, @args.merge(:separator => separator))
|
307
|
-
end
|
308
|
-
|
309
|
-
should "change underscores into its separator unless allowed" do
|
310
|
-
string = "#{Factory.string}_#{Factory.string}"
|
311
|
-
assert_equal string.gsub('_', '-'), subject.new(string, @args)
|
312
|
-
|
313
|
-
exp = string.gsub('_', '-')
|
314
|
-
assert_equal exp, subject.new(string, @args.merge(:allow_underscores => false))
|
315
|
-
|
316
|
-
assert_equal string, subject.new(string, @args.merge(:allow_underscores => true))
|
317
|
-
end
|
318
|
-
|
319
|
-
should "not allow multiple separators in a row" do
|
320
|
-
string = "#{Factory.string}--#{Factory.string}"
|
321
|
-
assert_equal string.gsub(/-{2,}/, '-'), subject.new(string, @args)
|
322
|
-
|
323
|
-
# remove separators that were added from changing invalid chars
|
324
|
-
invalid_chars = (Factory.integer(3) + 1).times.map{ NON_WORD_CHARS.sample }.join
|
325
|
-
string = "#{Factory.string}#{invalid_chars}#{Factory.string}"
|
326
|
-
assert_equal string.gsub(/[^\w]+/, '-'), subject.new(string, @args)
|
327
|
-
end
|
328
|
-
|
329
|
-
should "remove leading and trailing separators" do
|
330
|
-
string = "-#{Factory.string}-#{Factory.string}-"
|
331
|
-
assert_equal string[1..-2], subject.new(string, @args)
|
332
|
-
|
333
|
-
# remove separators that were added from changing invalid chars
|
334
|
-
invalid_char = NON_WORD_CHARS.sample
|
335
|
-
string = "#{invalid_char}#{Factory.string}-#{Factory.string}#{invalid_char}"
|
336
|
-
assert_equal string[1..-2], subject.new(string, @args)
|
337
|
-
end
|
338
|
-
|
339
|
-
end
|
340
|
-
|
341
|
-
end
|