Skip to content

Commit

Permalink
Merge pull request #22 from plicjo/master
Browse files Browse the repository at this point in the history
Document Adequate Exposure
  • Loading branch information
rwz committed Jun 22, 2015
2 parents 31eebe3 + c9c9710 commit db05385
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 0 deletions.
22 changes: 22 additions & 0 deletions lib/adequate_exposure/attribute.rb
Expand Up @@ -2,20 +2,42 @@ module AdequateExposure
class Attribute
attr_reader :name, :fetch, :ivar_name

# Public: Initialize an Attribute
#
# options - Hash of options for the Attribute
# :name - The String name of the Attribute instance
# :fetch - The Proc fetch to calculate
# the value of the Attribute instance.
# This is only called if the attribute's
# instance variable is not defined.
# :ivar_name - The String instance variable name that
# is associated with the attribute.
def initialize(options)
@name = options.fetch(:name)
@fetch = options.fetch(:fetch)
@ivar_name = options.fetch(:ivar_name)
end

# Public: The getter method for the Attribute.
#
# Returns the name of the Attribute as a Symbol.
def getter_method_name
name.to_sym
end

# Public: The setter method for the Attribute.
#
# Returns the name of the attribute as a Symbol with an appended '='.
def setter_method_name
"#{name}=".to_sym
end


# Public: Expose a getter and setter method for the Attribute
# on the passed in Controller class.
#
# klass - The Controller class where the Attribute getter and setter
# methods will be exposed.
def expose!(klass)
attribute = self

Expand Down
41 changes: 41 additions & 0 deletions lib/adequate_exposure/behavior.rb
@@ -1,10 +1,20 @@
module AdequateExposure
module Behavior
# Public: Fetches a scope.
#
# Finds an object. If it isn't found, the object gets instantiated.
#
# Returns the decorated object.
def fetch
instance = id ? find(id, computed_scope) : build(build_params, computed_scope)
decorate(instance)
end

# Public: Checks a params hash for an id attribute.
#
# Checks a hash of parameters for keys that represent an object's id.
#
# Returns the value of the id parameter, if it exists. Otherwise nil.
def id
params_id_key_candidates.each do |key|
value = params[key]
Expand All @@ -14,26 +24,57 @@ def id
nil
end

# Public: An object query. Essentially, this method is designed to be
# overridden.
#
# model - The Class to be scoped or queried.
#
# Returns the object scope.
def scope(model)
model
end

# Public: Converts a name into a standard Class name.
#
# Examples
# 'egg_and_hams'.model # => EggAndHam
#
# Returns a standard Class name.
def model
name.to_s.classify.constantize
end

# Public: Find an object on the supplied scope.
#
# id - The Integer id attribute of the desired object
# scope - The collection that will be searched.
#
# Returns the found object.
def find(id, scope)
scope.find(id)
end

# Public: Builds a new object on the passed-in scope.
#
# params - A Hash of attributes for the object to-be built.
# scope - The collection that will be searched.
#
# Returns the new object.
def build(params, scope)
scope.new(params)
end

# Public: Returns a decorated object. This method is designed to be
# overridden.
#
# Returns the decorated object.
def decorate(instance)
instance
end

# Public: Get all the parameters of the current request.
#
# Returns the controller's parameters for the current request.
def build_params
if controller.respond_to?(params_method_name, true) && !get_request?
controller.send(params_method_name)
Expand Down
19 changes: 19 additions & 0 deletions lib/adequate_exposure/context.rb
Expand Up @@ -2,14 +2,33 @@ module AdequateExposure
class Context
attr_reader :context, :attribute

# Public: Initialize a context.
#
# context - The Class where the attribute is defined.
# attribute - The attribute that will be accessed by a getter
# and setter.
def initialize(context, attribute)
@context, @attribute = context, attribute
end

# Public: Read an attribute on the context Class.
#
# Get an attribute's value. If the attribute's instance
# variable is not defined, it will create one,
# execute attribute#fetch, and assign the result
# to the instance variable.
#
# Returns the attribute's value.
def get
ivar_defined?? ivar_get : set(fetch_value)
end

# Public: Write to an attribute on the context Class.
#
# value - The value that will be set to the attribute's
# instance variable.
#
# Returns the attribute's value.
def set(value)
ivar_set(value)
end
Expand Down
27 changes: 27 additions & 0 deletions lib/adequate_exposure/controller.rb
Expand Up @@ -8,15 +8,42 @@ module Controller
end

module ClassMethods
# Public: Exposes an attribute to a controller Class.
#
# *args - An Array of attributes for the new exposure. See
# Exposure#initialize for attribute details.
# block - If supplied, the exposed attribute method executes
# the Proc when accessed.
#
# Returns the helper methods that are now defined on the class
# where this method is included.
def expose(*args, &block)
Exposure.expose! self, *args, &block
end

# Public: Exposes an attribute to a controller Class.
# The exposed methods are then set to a before_action
# callback.
#
# name - The String name of the Exposure instance.
# *args - An Array of attributes for the new exposure. See
# Exposure#initialize for attribute details.
# block - If supplied, the exposed attribute method executes
# the Proc when accessed.
#
# Sets the exposed attribute to a before_action callback in the
# controller.
def expose!(name, *args, &block)
expose name, *args, &block
before_action name
end

# Public: Configures an Exposure instance for a controller Class.
#
# name - The String name of the Exposure instance.
# options - The Hash of options to configure the Exposure instance.
#
# Returns the exposure configuration Hash.
def exposure_config(name, options)
store = self.exposure_configuration ||= {}
self.exposure_configuration = store.merge(name => options)
Expand Down
33 changes: 33 additions & 0 deletions lib/adequate_exposure/exposure.rb
Expand Up @@ -2,10 +2,38 @@ module AdequateExposure
class Exposure
attr_reader :controller, :options

# Public: Initializes an Exposure and makes it accessible to a controller.
# For each Exposure, a getter and setter is defined.
# Those getters and setters are made available to
# the controller as helper methods.
#
# *args - An Array of all parameters for the new Exposure. See
# #initialize.
# block - If supplied, the exposed attribute method executes
# the Proc when called.
#
# Returns a collection of exposed helper methods.
def self.expose!(*args, &block)
new(*args, &block).expose!
end

# Public: Initalize an Exposure with a hash of options.
#
# If a block is given, the Proc is assigned to value
# of options[name].
#
# The `asserts_*` section raise errors if the controller
# was initialized with an unacceptable options Hash.
#
# controller - The Controller class where methods will be exposed.
# name - The String name of the Exposure instance.
# fetch_block - Proc that will be executed if the exposed
# attribute has no value (default: nil).
# options - Hash of options for the Behavior of the exposed methods.
# block - If supplied, the exposed attribute method executes
# the Proc.
#
# Returns a normalized options Hash.
def initialize(controller, name, fetch_block=nil, **options, &block)
@controller = controller
@options = options.with_indifferent_access.merge(name: name)
Expand All @@ -22,6 +50,11 @@ def initialize(controller, name, fetch_block=nil, **options, &block)
normalize_options
end

# Public: Creates a getter and setter methods for the attribute.
# Those methods are made avaiable to the controller as
# helper methods.
#
# Returns a collection of exposed helper methods.
def expose!
expose_attribute!
expose_helper_methods!
Expand Down
17 changes: 17 additions & 0 deletions lib/adequate_exposure/flow.rb
Expand Up @@ -2,12 +2,24 @@ module AdequateExposure
class Flow
attr_reader :controller, :options, :name

# Public: Initialize a Flow. This object responds to missing
# methods errors and attempts to delegate them to other objects.
#
# controller - The Controller class where the method was called.
# options - The options Hash of the Exposure instance being called.
# name - The String name of the Exposure instance.
def initialize(controller, options)
@controller = controller
@options = options
@name = options.fetch(:name)
end

# Public: Attempts to re-delegate a method missing to the
# supplied block or the Behavior object.
#
# name - The String name of the Exposure instance.
# *args - The arguments given for the missing method.
# block - The Proc invoked by the method.
def method_missing(name, *args, &block)
if respond_to_missing?(name)
handle_flow_method(name, *args, &block)
Expand All @@ -16,6 +28,11 @@ def method_missing(name, *args, &block)
end
end

# Public: Checks if the Behavior class can handle the missing method.
#
# method_name - The name of method that has been called.
# include_private - Prevents this method from catching calls to private
# method (default: false).
def respond_to_missing?(method_name, include_private = false)
Behavior.method_defined?(method_name) || super
end
Expand Down

0 comments on commit db05385

Please sign in to comment.