Module: FactoryBurgers::Cheating
- Defined in:
- lib/factory_burgers/cheating.rb
Overview
This module contrains utilities to manipulate sequences that might fail for usage in development environments that do not roll back db transactions for factory creations.
Class Method Summary collapse
-
.advance_sequence(name, klass, column, sql: nil, regex: nil) ⇒ Object
Find the highest value of a sequence in the database and advance the sequence past it to avoid uniqueness violations.
-
.discover_sequences(factory) ⇒ Object
Given a factory, discover what sequences are used in its attributes.
- .find_highest_index_value(klass, column, sql, regex) ⇒ Object
-
.regex_pattern(sequence, numeric: true) ⇒ Object
For a sequence defined with { |ii| “foo#ii” }, ‘sql_condition` returns the regex /foo(d+)/.
-
.sql_condition(sequence, column) ⇒ Object
For a sequence defined with { |ii| “foo#ii” }, ‘sql_condition` returns the SQL fragemnt “<name> like ’foo%‘” TODO: does this work in pg, sqlite? TODO: support mongo as well.
Class Method Details
.advance_sequence(name, klass, column, sql: nil, regex: nil) ⇒ Object
Find the highest value of a sequence in the database and advance the sequence past it to avoid uniqueness violations. This is by no means foolproof nor performant; use it with care. There isn’t a good way to access the iterator state; instead, we measure how far we must advance the sequence and call ‘generate` that many times. This works well for sequential iterations, but more complex sequences might break this.
29 30 31 32 33 34 35 36 |
# File 'lib/factory_burgers/cheating.rb', line 29 def advance_sequence(name, klass, column, sql: nil, regex: nil) sequence = FactoryBot::Internal.sequences.find(name) sql ||= sql_condition(sequence, column) regex ||= regex_pattern(sequence) highest = find_highest_index_value(klass, column, sql, regex) or return nil highest&.times { FactoryBot.generate name } end |
.discover_sequences(factory) ⇒ Object
Given a factory, discover what sequences are used in its attributes. Since factories’ attributes are defined as blocks, there is no way to know which blocks use sequences without executing the block. We do this with SequenceCheater to take notes without actually using the seqence itself.
12 13 14 15 16 17 18 19 20 |
# File 'lib/factory_burgers/cheating.rb', line 12 def discover_sequences(factory) cheater = SequenceCheater.new attributes = factory.definition.attributes attributes.map do |attr| block = attr.instance_variable_get(:@block) cheater.instance_eval(&block) end return cheater.sequence_names end |
.find_highest_index_value(klass, column, sql, regex) ⇒ Object
38 39 40 41 |
# File 'lib/factory_burgers/cheating.rb', line 38 def find_highest_index_value(klass, column, sql, regex) matches = klass.where(sql).pluck(column).grep(regex) return matches.map { |value| value =~ regex && Regexp.last_match(1) }.map(&:to_i).max end |
.regex_pattern(sequence, numeric: true) ⇒ Object
For a sequence defined with { |ii| “foo#ii” }, ‘sql_condition` returns the regex /foo(d+)/
65 66 67 68 69 70 |
# File 'lib/factory_burgers/cheating.rb', line 65 def regex_pattern(sequence, numeric: true) wildcard = numeric ? "\\d" : "." proc = sequence.instance_variable_get(:@proc) injector = SequenceInjector.new("(#{wildcard}+)") return Regexp.new(proc.call(injector)) end |
.sql_condition(sequence, column) ⇒ Object
For a sequence defined with { |ii| “foo#ii” }, ‘sql_condition` returns the SQL fragemnt “<name> like ’foo%‘” TODO: does this work in pg, sqlite? TODO: support mongo as well
54 55 56 57 58 59 60 61 |
# File 'lib/factory_burgers/cheating.rb', line 54 def sql_condition(sequence, column) # This proc is defined by the block used in the sequence definition # This may be fragile, but may also be out only option proc = sequence.instance_variable_get(:@proc) injector = SequenceInjector.new("%") sql_value = proc.call(injector) return "#{column} like '#{sql_value}'" end |