Skip to content

Commit

Permalink
Move all persistence bindings to adapters
Browse files Browse the repository at this point in the history
  • Loading branch information
EPecherkin committed Oct 14, 2016
1 parent f1f2240 commit 56eaf84
Show file tree
Hide file tree
Showing 27 changed files with 406 additions and 126 deletions.
2 changes: 1 addition & 1 deletion .rubocop_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Metrics/MethodLength:
# Offense count: 2
# Configuration parameters: CountComments.
Metrics/ModuleLength:
Max: 110
Max: 114

# Offense count: 3
Metrics/PerceivedComplexity:
Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,30 @@ ActiveData has modular architecture, so it is required to include modules to obt
#### ReferencesMany
#### Interacting with ActiveRecord

### Persistence Adapters

Adapter definition syntax:
```ruby
ActiveData.persistence_adapter('ClassName') do |data_source, primary_key, scope_proc|
CustomAdapter.new(data_source, primary_key, scope_proc) # or anything that have similar interface to ActiveData::Model::Associations::PersistenceAdapters::Base
end
```
Where
`ClassName` - name of model class or one of ancestors
`data_source` - name of data source class
`primary_key` - key to search data
`scope_proc` - additional proc for filtering

All required interface for adapters described in `ActiveData::Model::Associations::PersistenceAdapters::Base`.

Adapter for ActiveRecord is `ActiveData::Model::Associations::PersistenceAdapters::ActiveRecord`.
```ruby
ActiveData.persistence_adapter('ActiveRecord::Base') do |data_source, primary_key, scope_proc|
PersistenceAdapters::ActiveRecord.new(data_source, primary_key, scope_proc)
end
```
So, all AR models will use `PersistenceAdapters::ActiveRecord` by default.

### Primary

### Persistence
Expand Down
8 changes: 6 additions & 2 deletions lib/active_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
require 'active_data/extensions'
require 'active_data/config'
require 'active_data/railtie' if defined? Rails
require 'active_data/model'
require 'active_data/persistence_adapters'

module ActiveData
BOOLEAN_MAPPING = {
Expand Down Expand Up @@ -127,9 +129,11 @@ def self.config
ActiveData::UUID.parse_int value
end
end
end

require 'active_data/model'
persistence_adapter('ActiveRecord::Base') do |data_source, primary_key, scope_proc|
ActiveData::Model::Associations::PersistenceAdapters::ActiveRecord.new(data_source, primary_key, scope_proc)
end
end

ActiveSupport.on_load :active_record do
require 'active_data/active_record/associations'
Expand Down
15 changes: 14 additions & 1 deletion lib/active_data/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ class Config
include Singleton

attr_accessor :include_root_in_json, :i18n_scope, :logger, :primary_attribute,
:_normalizers, :_typecasters
:_normalizers, :_typecasters, :_persistence_adapters

def self.delegated
public_instance_methods - superclass.public_instance_methods - Singleton.public_instance_methods
Expand All @@ -16,6 +16,7 @@ def initialize
@primary_attribute = :id
@_normalizers = {}
@_typecasters = {}
@_persistence_adapters = {}
end

def normalizer(name, &block)
Expand All @@ -26,6 +27,18 @@ def normalizer(name, &block)
end
end

def persistence_adapter(klass, &block)
klass_name = klass.to_s.camelize
if block
_persistence_adapters[klass_name] = block
else
klass_const = klass_name.constantize
adapter = _persistence_adapters.values_at(*klass_const.ancestors.map(&:to_s)).compact.first
raise PersistenceAdapterMissing, klass unless adapter
adapter
end
end

def typecaster(*classes, &block)
classes = classes.flatten
if block
Expand Down
13 changes: 13 additions & 0 deletions lib/active_data/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,17 @@ def initialize(*classes)
EOS
end
end

class PersistenceAdapterMissing < NoMethodError
def initialize(data_source)
super <<-EOS
Could not find persistence adapter for #{data_source}
You can define it with:
ActiveData.persistence_adapter('#{data_source}') do |data_source, primary_key, scope_proc|
# do some staff with value and options
end
EOS
end
end
end
3 changes: 2 additions & 1 deletion lib/active_data/model/associations.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
require 'active_data/model/associations/collection/proxy'
require 'active_data/model/associations/collection/embedded'
require 'active_data/model/associations/collection/referenced'

require 'active_data/model/associations/reflections/base'
require 'active_data/model/associations/reflections/embed_reflection'
require 'active_data/model/associations/reflections/embeds_one'
require 'active_data/model/associations/reflections/embeds_many'
require 'active_data/model/associations/reflections/reference_reflection'
Expand All @@ -13,6 +13,7 @@
require 'active_data/model/associations/embed_association'
require 'active_data/model/associations/embeds_one'
require 'active_data/model/associations/embeds_many'
require 'active_data/model/associations/reference_association'
require 'active_data/model/associations/references_one'
require 'active_data/model/associations/references_many'

Expand Down
26 changes: 0 additions & 26 deletions lib/active_data/model/associations/collection/referenced.rb

This file was deleted.

4 changes: 3 additions & 1 deletion lib/active_data/model/associations/embeds_one.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ def load_target
def default
return if evar_loaded?

default = reflection.default(owner) or return
default = reflection.default(owner)

return unless default

object = if default.is_a?(reflection.klass)
default
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
require 'active_data/model/associations/persistence_adapters/active_record/referenced_proxy'

module ActiveData
module Model
module Associations
module PersistenceAdapters
class ActiveRecord < Base
TYPES = {
integer: Integer,
float: Float,
decimal: BigDecimal,
datetime: Time,
timestamp: Time,
time: Time,
date: Date,
text: String,
string: String,
binary: String,
boolean: Boolean
}.freeze

alias_method :data_type, :data_source

def build(attributes)
data_source.new(attributes)
end

def scope(owner, source)
scope = data_source.unscoped

if scope_proc
scope = if scope_proc.arity.zero?
scope.instance_exec(&scope_proc)
else
scope.instance_exec(owner, &scope_proc)
end
end

scope.where(primary_key => source)
end

def identify(object)
object[primary_key]
end

def primary_key
@primary_key ||= :id
end

def primary_key_type
column = data_source.columns_hash[primary_key.to_s]
TYPES[column.type]
end

def referenced_proxy(association)
ReferencedProxy.new(association)
end
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module ActiveData
module Model
module Associations
module PersistenceAdapters
class ActiveRecord < Base
class ReferencedProxy < ActiveData::Model::Associations::Collection::Proxy
# You can't create data directly through ActiveRecord::Relation
METHODS_EXCLUDED_FROM_DELEGATION = %w(build create create!).map(&:to_sym).freeze

attr_reader :association
delegate :scope, to: :@association

def method_missing(method, *args, &block)
delegate_to_scope?(method) ? scope.send(method, *args, &block) : super
end

def respond_to_missing?(method, include_private = false)
delegate_to_scope?(method) || super
end

private

def delegate_to_scope?(method)
METHODS_EXCLUDED_FROM_DELEGATION.exclude?(method) && scope.respond_to?(method)
end
end
end
end
end
end
end
49 changes: 49 additions & 0 deletions lib/active_data/model/associations/persistence_adapters/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
module ActiveData
module Model
module Associations
module PersistenceAdapters
class Base
attr_reader :data_source, :primary_key, :scope_proc

def initialize(data_source, primary_key, scope_proc = nil)
@data_source = data_source
@primary_key = primary_key
@scope_proc = scope_proc
end

def build(_attributes)
raise NotImplementedError, 'Should be implemented in inhereted adapter. Build new instance of data object by attributes'
end

def scope(_owner, _source)
raise NotImplementedError, 'Should be implemented in inhereted adapter. Better to be Enumerable'
end

def find_one(owner, identificator)
scope(owner, identificator).first
end

def find_all(owner, identificators)
scope(owner, identificators).to_a
end

def identify(_object)
raise NotImplementedError, 'Should be implemented in inhereted adapter. Field to be used as primary_key for object'
end

def data_type
raise NotImplementedError, 'Should be implemented in inhereted adapter. Type of data object for type_check'
end

def primary_key_type
raise NotImplementedError, 'Should be implemented in inhereted adapter. Ruby data type'
end

def referenced_proxy
raise NotImplementedError, 'Should be implemented in inhereted adapter. Object to manage proxying of methods to scope.'
end
end
end
end
end
end
11 changes: 11 additions & 0 deletions lib/active_data/model/associations/reference_association.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module ActiveData
module Model
module Associations
class ReferenceAssociation < Base
def scope(source = read_source)
reflection.persistence_adapter.scope(owner, source)
end
end
end
end
end
Loading

0 comments on commit 56eaf84

Please sign in to comment.