Skip to content

Commit

Permalink
Accept inclusion/exclusion association chains as hashes or arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
Envek committed Nov 6, 2024
1 parent 2114fcd commit da93ecb
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 5 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

Excluded associations can be re-included by `include` with matching pattern.

- Exclusion and inclusion patterns can be specified as hashes and/or arrays. [@Envek]

```ruby
config.root('Forum', featured: true) do |forum|
forum.include(parent: {questions: %i[answers votes]})
end
```

Which is equivalent to:

```ruby
config.root('Forum', featured: true) do |forum|
forum.include(/\Aforum(\.parent(\.questions(\.answers))?)?)?\z/)
forum.include(/\Aforum(\.parent(\.questions(\.votes))?)?)?\z/)
end
```

- Print reason of association exclusion or inclusion in verbose mode. [@Envek]

### Fixed
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,10 @@ EvilSeed.configure do |config|
# example: "forum.users.questions"
root.exclude(/\btracking_pixels\b/, 'forum.popular_questions', /\Aforum\.parent\b/)

# Include back only certain associations
root.include(/\Aforum(\.parent(\.questions(\.answers)?)?)?\z/)
# Include back only certain association chains
root.include(parent: {questions: %i[answers votes]})
# which is the same as
root.include(/\Aforum(\.parent(\.questions(\.(answers|votes))?)?)?\z/)

# It's possible to limit the number of included into dump has_many and has_one records for every association
# Note that belongs_to records for all not excluded associations are always dumped to keep referential integrity.
Expand Down
49 changes: 47 additions & 2 deletions lib/evil_seed/configuration/root.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,29 @@ def initialize(model, dont_nullify, *constraints)
# Exclude some of associations from the dump
# @param association_patterns Array<String, Regex> Patterns to exclude associated models from dump
def exclude(*association_patterns)
@exclusions += association_patterns
association_patterns.each do |pattern|
case pattern
when String, Regexp
@exclusions << pattern
else
path_prefix = model.constantize.model_name.singular
@inclusions += compile_patterns(pattern, prefix: path_prefix).map { |p| Regexp.new(/\A#{p}\z/) }
end
end
end

# Include some excluded associations back to the dump
# @param association_patterns Array<String, Regex> Patterns to exclude associated models from dump
def include(*association_patterns)
@inclusions += association_patterns
association_patterns.each do |pattern|
case pattern
when String, Regexp
@inclusions << pattern
else
path_prefix = model.constantize.model_name.singular
@inclusions += compile_patterns(pattern, prefix: path_prefix).map { |p| Regexp.new(/\A#{p}\z/) }
end
end
end

def exclude_has_relations
Expand Down Expand Up @@ -76,6 +92,35 @@ def excluded_has_relations?
def excluded_optional_belongs_to?
@excluded_optional_belongs_to
end

private

def compile_patterns(pattern, prefix: "")
case pattern
when String, Symbol
["#{prefix}(?:\\.#{pattern.to_s})?"]
when Regexp
["#{prefix}(?:\\.(?:#{pattern.source}))?"]
when Array
pattern.map { |p| compile_patterns(p, prefix: prefix) }.flatten
when Hash
pattern.map do |k, v|
next nil unless v
subpatterns = compile_patterns(v)
next "#{prefix}(?:\\.#{k})?" if subpatterns.empty?

subpatterns.map do |p|
"#{prefix}(?:\\.#{k}#{p})?"
end
end.compact.flatten
when false, nil
nil
when true
[prefix]
else
raise ArgumentError, "Unknown pattern type: #{pattern.class} for #{pattern.inspect}"
end
end
end
end
end
2 changes: 1 addition & 1 deletion test/evil_seed/dumper_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def test_limits_being_applied
def test_it_applies_unscoping_and_inclusions
configuration = EvilSeed::Configuration.new
configuration.root('Forum', name: 'Descendant forum') do |root|
root.include(/forum(\.parent(\.questions(\.answers)?)?)?\z/)
root.include(parent: {questions: :answers})
root.exclude(/.\..+/)
end
configuration.unscoped = true
Expand Down

0 comments on commit da93ecb

Please sign in to comment.