module Protip::Resource::ClassMethods

Constants

VALID_ACTIONS

Attributes

base_path[W]
belongs_to_associations[R]
belongs_to_polymorphic_associations[R]
client[RW]
message[R]
nested_resources[R]
transformer[W]

Public Class Methods

new(resource_class) click to toggle source
# File lib/protip/resource.rb, line 250
def initialize(resource_class)
  @resource_class = resource_class
  @associations = []
end

Public Instance Methods

base_path() click to toggle source
# File lib/protip/resource.rb, line 71
def base_path
  if @base_path == nil
    raise(RuntimeError.new 'Base path not yet set')
  else
    @base_path.gsub(/\/$/, '')
  end
end
transformer() click to toggle source
# File lib/protip/resource.rb, line 79
def transformer
  defined?(@transformer) ? @transformer : ::Protip.default_transformer
end

Private Instance Methods

belongs_to(association_name, options = {}) click to toggle source
# File lib/protip/resource.rb, line 237
def belongs_to(association_name, options = {})
  association = ::Protip::Resource::Associations::BelongsToAssociation.new(self, association_name, **options)
  association.define_accessors!
  @belongs_to_associations.add association
  association
end
belongs_to_polymorphic(association_name, options = {}, &block) click to toggle source
# File lib/protip/resource.rb, line 244
def belongs_to_polymorphic(association_name, options = {}, &block)
  # We evaluate the block in the context of a wrapper that
  # stores simple belongs-to associations as they're being
  # created.
  nested_association_creator = Class.new do
    attr_reader :associations
    def initialize(resource_class)
      @resource_class = resource_class
      @associations = []
    end
    def belongs_to(*args)
      # Just forward the belongs_to call and store the result so we can pass it to the polymorphic association
      @associations << @resource_class.send(:belongs_to, *args)
    end
  end.new(self)

  nested_association_creator.instance_eval(&block)

  association = ::Protip::Resource::Associations::BelongsToPolymorphicAssociation.new self,
    association_name, nested_association_creator.associations, **options
  association.define_accessors!
  @belongs_to_polymorphic_associations.add association
  association
end
collection(action:, method:, request: nil, response: nil) click to toggle source
# File lib/protip/resource.rb, line 216
def collection(action:, method:, request: nil, response: nil)
  if request
    define_singleton_method action do |request_params = {}|
      message = nil
      if request_params.is_a?(request) # Message provided directly
        message = request_params
      else # Parameters provided by hash
        decorator = ::Protip::Decorator.new(request.new, transformer)
        decorator.assign_attributes request_params
        message = decorator.message
      end
      ::Protip::Resource::ExtraMethods.collection self,
        action, method, message, response
    end
  else
    define_singleton_method action do
      ::Protip::Resource::ExtraMethods.collection self, action, method, nil, response
    end
  end
end
define_attribute_accessors(message) click to toggle source

Define attribute readers/writers

# File lib/protip/resource.rb, line 129
def define_attribute_accessors(message)
  message.descriptor.each do |field|
    def_delegator :@decorator, :"#{field.name}"
    def_delegator :@decorator, :"#{field.name}?"

    define_method "#{field.name}=" do |new_value|
      old_value = self.message[field.name] # Only compare the raw values
      @decorator.send("#{field.name}=", new_value)
      new_value = self.message[field.name]

      # Need to check that types are the same first, otherwise protobuf gets mad comparing
      # messages with non-messages
      send("#{field.name}_will_change!") unless new_value.class == old_value.class && new_value == old_value
    end

    # needed for ActiveModel::Dirty
    define_attribute_method field.name
  end
end
define_oneof_group_methods(message) click to toggle source

Allow calls to oneof groups to get the set oneof field

# File lib/protip/resource.rb, line 122
def define_oneof_group_methods(message)
  message.descriptor.each_oneof do |oneof_field|
    def_delegator :@decorator, :"#{oneof_field.name}"
  end
end
define_resource_query_methods(query, actions) click to toggle source

For index/show, we want a different number of method arguments depending on whether a query message was provided.

# File lib/protip/resource.rb, line 151
def define_resource_query_methods(query, actions)
  if query
    if actions.include?(:show)
      define_singleton_method :find do |id, query_params = {}|
        message = nil
        if query_params.is_a?(query)
          message = query_params
        else
          decorator = ::Protip::Decorator.new(query.new, transformer)
          decorator.assign_attributes query_params
          message = decorator.message
        end
        ::Protip::Resource::SearchMethods.show(self, id, message)
      end
    end

    if actions.include?(:index)
      define_singleton_method :all do |query_params = {}|
        message = nil
        if query_params.is_a?(query)
          message = query_params
        else
          decorator = ::Protip::Decorator.new(query.new, transformer)
          decorator.assign_attributes query_params
          message = decorator.message
        end
        ::Protip::Resource::SearchMethods.index(self, message)
      end
    end
  else
    if actions.include?(:show)
      define_singleton_method :find do |id|
        ::Protip::Resource::SearchMethods.show(self, id, nil)
      end
    end

    if actions.include?(:index)
      define_singleton_method :all do
        ::Protip::Resource::SearchMethods.index(self, nil)
      end
    end
  end
end
member(action:, method:, request: nil, response: nil) click to toggle source
# File lib/protip/resource.rb, line 195
def member(action:, method:, request: nil, response: nil)
  if request
    define_method action do |request_params = {}|
      message = nil
      if request_params.is_a?(request) # Message provided directly
        message = request_params
      else # Parameters provided by hash
        decorator = ::Protip::Decorator.new(request.new, self.class.transformer)
        decorator.assign_attributes request_params
        message = decorator.message
      end
      ::Protip::Resource::ExtraMethods.member self,
        action, method, message, response
    end
  else
    define_method action do
      ::Protip::Resource::ExtraMethods.member self, action, method, nil, response
    end
  end
end
references_through_one_of(id_field, options = {}) click to toggle source
# File lib/protip/resource.rb, line 269
def references_through_one_of(id_field, options = {})
  ::Protip::Resource::Associations::ReferencesThroughOneOfAssociation.new(self, id_field, options)
    .define_accessors!
end
resource(actions:, message:, query: nil, nested_resources: {}) click to toggle source

Primary entry point for defining resourceful behavior.

# File lib/protip/resource.rb, line 86
def resource(actions:, message:, query: nil, nested_resources: {})
  raise RuntimeError.new('Only one call to `resource` is allowed') if defined?(@message) && @message
  validate_actions!(actions)
  validate_nested_resources!(nested_resources)

  @message = message
  @nested_resources = nested_resources

  define_attribute_accessors(@message)
  define_oneof_group_methods(@message)
  define_resource_query_methods(query, actions)

  include(::Protip::Resource::Creatable) if actions.include?(:create)
  include(::Protip::Resource::Updatable) if actions.include?(:update)
  include(::Protip::Resource::Destroyable) if actions.include?(:destroy)
end
validate_actions!(actions) click to toggle source
# File lib/protip/resource.rb, line 114
def validate_actions!(actions)
  actions.map!{|action| action.to_sym}
  (actions - VALID_ACTIONS).each do |action|
    raise ArgumentError.new("Unrecognized action: #{action}")
  end
end
validate_nested_resources!(nested_resources) click to toggle source
# File lib/protip/resource.rb, line 103
def validate_nested_resources!(nested_resources)
  nested_resources.each do |key, resource_klass|
    unless key.is_a?(Symbol)
      raise "#{key} must be a Symbol, but is a #{key.class}"
    end
    unless resource_klass < ::Protip::Resource
      raise "#{resource_klass} is not a Protip::Resource"
    end
  end
end