Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow importing filters from other interactions #104

Closed
tfausak opened this issue Jan 15, 2014 · 4 comments
Closed

Allow importing filters from other interactions #104

tfausak opened this issue Jan 15, 2014 · 4 comments
Assignees
Milestone

Comments

@tfausak
Copy link
Collaborator

tfausak commented Jan 15, 2014

Sometimes it's useful to share filters between interactions. Usually inheritance doesn't work. My current workaround looks like this:

class Alpha < ActiveInteraction::Base
  module Filters
    extend ActiveSupport::Concern
    included { boolean :gamma }
  end
  include Filters
  # ...
end
class Beta < ActiveInteraction::Base
  include Alpha::Filters
  boolean :delta
  # ...
end
@tfausak
Copy link
Collaborator Author

tfausak commented Jan 15, 2014

This is sort of like #52, but not quite. It's possible to use the .filters method to do this without modules:

class Beta < ActiveInteraction::Base
  Alpha.filters.each do |filter|
    send(filter.class.slug, filter.name, filter.options)
  end
  # ...
end

Of course that doesn't work when blocks are involved, but it gets you most of the way there.

@tfausak
Copy link
Collaborator Author

tfausak commented Jan 16, 2014

Thanks to #112, I've found what feels like an elegant solution to this.

We can use const_missing on Base to catch references to Filters and return an anonymous module. That module will add it's filters when it's included. Here's how it looks:

module ActiveInteraction
  class Base
    class << self
      def const_defined?(name, *)
        name.to_sym == :Filters || super
      end

      def const_missing(name)
        return super unless const_defined?(name)

        source_filters = self.filters

        Module.new do
          extend ActiveSupport::Concern

          included do
            filters.merge!(source_filters)
          end
        end
      end
    end
  end
end

@tfausak
Copy link
Collaborator Author

tfausak commented Jan 20, 2014

@justinsteffy proposed a good alternate solution. Instead of including a dynamically created module, why not add a class method to ActiveInteraction::Base? It might look like this:

module ActiveInteraction
  class Base
    def self.import(other, only: nil, except: nil)
      # ...
    end
  end
end

By default it would import all of the filters from the other interaction into the current one. If you supplied only, it would only import those. If you supplied except, it would import all except those. This borrows heavily from Python's import.

Here's how it might look to use it:

class Alpha < ActiveInteraction::Base
  boolean :gamma
  # ...
end
class Beta < ActiveInteraction::Base
  import Alpha
  # import Alpha, only: [:gamma]
  # import Alpha, except: [:gamma]
  boolean :delta
  # ...
end

@ghost ghost assigned tfausak Jan 20, 2014
@tfausak
Copy link
Collaborator Author

tfausak commented Jan 21, 2014

Here's a basic implementation:

def import(klass, only: nil, except: nil)
  other_filters = klass.filters.dup
  other_filters.select! { |k, _| only.include?(k) } if only
  other_filters.reject! { |k, _| except.include?(k) } if except

  filters.merge!(other_filters)
end

tfausak added a commit that referenced this issue Jan 21, 2014
Fix #104; allow importing filters from another interaction
@tfausak tfausak closed this as completed Jan 21, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant