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.
Files changed (59) hide show
  1. checksums.yaml +7 -7
  2. data/.l.yml +8 -0
  3. data/.rubocop.yml +3 -0
  4. data/.t.yml +6 -0
  5. data/Gemfile +21 -8
  6. data/README.md +252 -3
  7. data/ardb.gemspec +14 -10
  8. data/bin/ardb +3 -1
  9. data/lib/ardb.rb +110 -80
  10. data/lib/ardb/adapter/base.rb +73 -47
  11. data/lib/ardb/adapter/mysql.rb +4 -17
  12. data/lib/ardb/adapter/postgresql.rb +51 -46
  13. data/lib/ardb/adapter/sqlite.rb +11 -15
  14. data/lib/ardb/adapter_spy.rb +18 -30
  15. data/lib/ardb/cli.rb +29 -24
  16. data/lib/ardb/cli/clirb.rb +19 -17
  17. data/lib/ardb/cli/commands.rb +308 -129
  18. data/lib/ardb/db_tests.rb +4 -4
  19. data/lib/ardb/default_order_by.rb +13 -21
  20. data/lib/ardb/migration.rb +15 -16
  21. data/lib/ardb/record_spy.rb +46 -61
  22. data/lib/ardb/relation_spy.rb +27 -31
  23. data/lib/ardb/require_autoloaded_active_record_files.rb +174 -58
  24. data/lib/ardb/test_helpers.rb +13 -14
  25. data/lib/ardb/use_db_default.rb +10 -19
  26. data/lib/ardb/version.rb +3 -1
  27. data/script/determine_autoloaded_active_record_files.rb +31 -24
  28. data/test/helper.rb +6 -13
  29. data/test/support/factory.rb +4 -3
  30. data/test/support/fake_schema.rb +3 -1
  31. data/test/support/postgresql/migrations/{.gitkeep → .keep} +0 -0
  32. data/test/support/postgresql/schema.rb +2 -1
  33. data/test/support/postgresql/setup_test_db.rb +17 -15
  34. data/test/support/relative_require_test_db_file.rb +1 -0
  35. data/test/support/require_test_db_file.rb +1 -0
  36. data/test/system/.keep +0 -0
  37. data/test/unit/adapter/base_tests.rb +83 -55
  38. data/test/unit/adapter/mysql_tests.rb +4 -19
  39. data/test/unit/adapter/postgresql_tests.rb +21 -30
  40. data/test/unit/adapter/sqlite_tests.rb +5 -11
  41. data/test/unit/adapter_spy_tests.rb +6 -17
  42. data/test/unit/ardb_tests.rb +81 -53
  43. data/test/unit/cli_tests.rb +232 -157
  44. data/test/unit/db_tests_tests.rb +7 -7
  45. data/test/unit/default_order_by_tests.rb +21 -20
  46. data/test/unit/migration_tests.rb +17 -18
  47. data/test/unit/record_spy_tests.rb +36 -34
  48. data/test/unit/relation_spy_tests.rb +40 -63
  49. data/test/unit/test_helpers_tests.rb +7 -15
  50. data/test/unit/use_db_default_tests.rb +22 -17
  51. metadata +117 -84
  52. data/lib/ardb/has_slug.rb +0 -107
  53. data/lib/ardb/migration_helpers.rb +0 -77
  54. data/lib/ardb/pg_json.rb +0 -90
  55. data/test/support/postgresql/pg_json_migrations/20160519133432_create_pg_json_migrate_test.rb +0 -13
  56. data/test/system/pg_json_tests.rb +0 -85
  57. data/test/unit/has_slug_tests.rb +0 -341
  58. data/test/unit/migration_helpers_tests.rb +0 -65
  59. data/test/unit/pg_json_tests.rb +0 -39
@@ -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
@@ -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
@@ -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
@@ -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