Skip to content

Commit

Permalink
Reorg adapters. adapters/ => orm_adapters/, adapter_map keeps track o…
Browse files Browse the repository at this point in the history
…f adapters, PickleAdapter => PickleOrmAdapter, factory adapters live in adapters/
  • Loading branch information
ianwhite committed Aug 22, 2010
1 parent 8fb35c0 commit 60ddcb3
Show file tree
Hide file tree
Showing 10 changed files with 231 additions and 156 deletions.
151 changes: 23 additions & 128 deletions lib/pickle/adapter.rb
@@ -1,137 +1,32 @@
require 'active_support/core_ext'

module Pickle
# Abstract Factory adapter class, if you have a factory type setup, you
# can easily create an adaptor to make it work with Pickle.
# Pickle creates a new adapter instance for each 'factory name' that is used. The adapter instance has
# responsibility for making new models, and for finding models.
#
# The factory adaptor must have a #factories class method that returns
# its instances, and each instance must respond to:
# The finding responsibilities are handled by OrmAdapter::AdapterMethods
#
# #name : identifies the factory by name (default is attr_reader)
# #klass : returns the associated model class for this factory (default is attr_reader)
# #create(attrs = {}) : returns a newly created object
# The creating repsonsibilities differ depending on the factory farmework you are using. Pickle provides:
#
# @see pickle/adapters/machinist
# @see pickle/adapters/factory_girl
# @see pickle/adapters/orm
#
# To create a new adapter:
# * it must set @model_class, and @name on initialization
# * it must implement the public method #make
# * if it is set as a Pickle::Config.adapter_class, then it must implement the class method #adapters that returns all of its adapter instances
class Adapter
attr_reader :name, :klass

def create(attrs = {})
raise NotImplementedError, "create and return an object with the given attributes"
end

if respond_to?(:class_attribute)
class_attribute :model_classes
else
cattr_writer :model_classes
end

self.model_classes = nil

# Include this module into your ORM adapter
# this will register the adapter with pickle and it will be picked up for you
# To create an adapter you should create an inner constant "PickleAdapter"
#
# e.g. ActiveRecord::Base::PickleAdapter
#
# @see pickle/adapters/active_record
# @see pickle/adapters/datamapper
# @see pickle/adapters/mongoid
module Base
def self.included(base)
adapters << base
end

# A collection of registered adapters
def self.adapters
@@adapters ||= []
end
include OrmAdapter::AdapterMethods

attr_reader :model_class, :name

def make(attributes = {})
raise NotImplementedError, "return a newly made model"
end

class << self
def factories
raise NotImplementedError, "return an array of factory adapter objects"
end

def model_classes
@@model_classes ||= self::Base.adapters.map{ |a| a.model_classes }.flatten
end

# Returns the column names for the given ORM model class.
def column_names(klass)
klass.const_get(:PickleAdapter).column_names(klass)
end

def get_model(klass, id)
klass.const_get(:PickleAdapter).get_model(klass, id)
end

def find_first_model(klass, conditions)
klass.const_get(:PickleAdapter).find_first_model(klass, conditions)
end

def find_all_models(klass, conditions)
klass.const_get(:PickleAdapter).find_all_models(klass, conditions)
end

def create_model(klass, attributes)
klass.const_get(:PickleAdapter).create_model(klass, attributes)
end
end

# machinist adapter
class Machinist < Adapter
def self.factories
factories = []
model_classes.each do |klass|
if blueprints = klass.instance_variable_get('@blueprints')
blueprints.keys.each {|blueprint| factories << new(klass, blueprint)}
end
end
factories
end

def initialize(klass, blueprint)
@klass, @blueprint = klass, blueprint
@name = @klass.name.underscore.gsub('/','_')
@name = "#{@blueprint}_#{@name}" unless @blueprint == :master
end

def create(attrs = {})
if @klass.respond_to?('make!')
@klass.send(:make!, @blueprint, attrs)
else
@klass.send(:make, @blueprint, attrs)
end
end
end

# factory-girl adapter
class FactoryGirl < Adapter
def self.factories
(::Factory.factories.values rescue []).map {|factory| new(factory)}
end

def initialize(factory)
@klass, @name = factory.build_class, factory.factory_name.to_s
end

def create(attrs = {})
Factory(@name, attrs)
end
end

# ORM adapter. If you have no factory adapter, you can use this adapter to
# use your orm as 'factory' - ie create objects
class Orm < Adapter
def self.factories
model_classes.map{|k| new(k)}
end

def initialize(klass)
@klass, @name = klass, klass.name.underscore.gsub('/','_')
end

def create(attrs = {})
Pickle::Adapter.create_model(@klass, attrs)
def adapters
raise NotImplementedError, "return an array of adapter instances"
end
end
end
end
end
23 changes: 23 additions & 0 deletions lib/pickle/adapter_map.rb
@@ -0,0 +1,23 @@
module Pickle
# register of adapters
class AdapterMap < Hash
attr_reader :adapter_classes

def initialize(adapter_classes)
@adapter_classes = adapter_classes
create_adapter_map
end

def reload
clear
create_adapter_map
end

private
def create_adapter_map
adapter_classes.reverse do |adapter_class|
self.merge(adapter_class.adapters.inject({}) {|map, adapter| map.merge(adapter.name => adapter)})
end
end
end
end
18 changes: 18 additions & 0 deletions lib/pickle/adapters/factory_girl.rb
@@ -0,0 +1,18 @@
module Pickle
class Adapter
# factory-girl adapter
class FactoryGirl < Adapter
def self.adapters
(::Factory.factories.values rescue []).map {|factory| new(factory)}
end

def initialize(factory)
@model_class, @name = factory.build_class, factory.factory_name.to_s
end

def make(attrs = {})
Factory(@model_class, attrs)
end
end
end
end
29 changes: 29 additions & 0 deletions lib/pickle/adapters/machinist.rb
@@ -0,0 +1,29 @@
module Pickle
class Adapter
class Machinist < Adapter
def self.adapters
adapters = []
Pickle::OrmAdapter.model_classes.each do |model_class|
if blueprints = klass.instance_variable_get('@blueprints')
blueprints.keys.each {|blueprint| adapter << new(model_class, blueprint)}
end
end
adapter
end

def initialize(model_class, blueprint)
@model_class, @blueprint = klass, blueprint
@name = @model_class.name.underscore.gsub('/','_')
@name = "#{@blueprint}_#{@name}" unless @blueprint == :master
end

def make(attrs = {})
begin
@model_class.send(:make!, @blueprint, attrs) # Machinist 2
rescue
@model_class.send(:make, @blueprint, attrs)
end
end
end
end
end
19 changes: 19 additions & 0 deletions lib/pickle/adapters/orm.rb
@@ -0,0 +1,19 @@
module Pickle
class Adapter
# ORM adapter. If you have no factory framework, you can use this adapter to
# use your orm as 'factory' - ie create objects
class Orm < Adapter
def self.adapters
Pickle::OrmAdapter.model_classes.map{|k| new(k)}
end

def initialize(klass)
@klass, @name = klass, klass.name.underscore.gsub('/','_')
end

def make(attrs = {})
model_class.const_get(:PickleOrmAdapter).create_model(model_class, attrs)
end
end
end
end
50 changes: 34 additions & 16 deletions lib/pickle/config.rb
@@ -1,3 +1,5 @@
require 'pickle/adapter_map'

module Pickle
class Config
attr_writer :adapters, :factories, :aliases, :labels, :mappings, :predicates
Expand All @@ -10,15 +12,46 @@ def configure(&block)
yield(self)
end

# hash of factory names => adapter instances
#
# defaults to all adapters available from adapter classes specifies by #adapters.
# It's sublcass of Hash, so you can add your own adapters in an ad-hoc manner.
#
# Config.new do |c|
# c.adapters = [:machinist]
# c.adapter_map['file'] = MyFileAdapter.new
# end
#
# @return Hash
def adapter_map
@adapter_map ||= AdapterMap.new(adapter_classes)
end

# adapters that pickle should use to create models
#
# set this to symbols found in pickle/adapters, or adapter classes
#
# Config.new do |c|
# c.adapters = [:factory_girl, MyAdapter]
# end
#
# @return Array of symbols or adapter classes
def adapters
@adapters ||= [:machinist, :factory_girl, :orm]
end

# returns adapter classes
# @see #adapters
# @return Array of adapter classes
def adapter_classes
adapters.map {|a| a.is_a?(Class) ? a : "pickle/adapter/#{a}".classify.constantize}
adapters.map do |adapter|
if adapter.is_a?(Class)
adapter
else
require "pickle/adapters/#{adapter}"
"pickle/adapter/#{adapter}".classify.constantize
end
end
end

# list of predicate names that the pickle parser should be aware of
Expand Down Expand Up @@ -78,19 +111,4 @@ def map(*args)
end
end
end

# map of all of the adapter instances that pickle is aware of. The key is
# a factory name (usually an underscored model class name, but could be a named factory,
# or named blueprint)
#
# e.g. { 'user' => Pickle::Adapter::Machinist.new(User, :master), 'admin_user' => Pickle::Adapter::Machinist.new(User, :admin) }
#
# @return Hash
#def factories
# @factories ||= adapter_classes.reverse.inject({}) do |factories, adapter|
# factories.merge(adapter.factories.inject({}){|h, f| h.merge(f.name => f)})
# end
#end


end

0 comments on commit 60ddcb3

Please sign in to comment.