Skip to content

Commit

Permalink
Extract model behavior to Rasti::Model gem
Browse files Browse the repository at this point in the history
  • Loading branch information
Gabriel Naiman committed Mar 5, 2021
1 parent 305f6e3 commit 7c96610
Show file tree
Hide file tree
Showing 16 changed files with 102 additions and 288 deletions.
5 changes: 4 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
source 'https://rubygems.org'

# Specify your gem's dependencies in rasti-db.gemspec
gemspec
gemspec

gem 'rasti-types', path: '../types'
gem 'rasti-model', path: '../model'
15 changes: 8 additions & 7 deletions lib/rasti/db.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'sequel'
require 'rasti-model'
require 'consty'
require 'time'
require 'timing'
Expand All @@ -15,13 +16,13 @@ module DB
extend MultiRequire
extend ClassConfig

require_relative 'db/query'
require_relative_pattern 'db/relations/*'
require_relative_pattern 'db/type_converters/postgres_types/*'
require_relative_pattern 'db/type_converters/sqlite_types/*'
require_relative 'db/nql/nodes/constants/base'
require_relative_pattern 'db/nql/filter_condition_strategies/types/*'
require_relative_pattern 'db/**/*'
require_relative 'db/query'
require_relative_pattern 'db/relations/*'
require_relative_pattern 'db/type_converters/postgres_types/*'
require_relative_pattern 'db/type_converters/sqlite_types/*'
require_relative 'db/nql/nodes/constants/base'
require_relative_pattern 'db/nql/filter_condition_strategies/types/*'
require_relative_pattern 'db/**/*'

attr_config :type_converters, []
attr_config :nql_filter_condition_strategy, nil
Expand Down
20 changes: 10 additions & 10 deletions lib/rasti/db/collection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def collection_name
end

def collection_attributes
@collection_attributes ||= model.attributes - relations.keys - computed_attributes.keys
@collection_attributes ||= model.attribute_names - relations.keys - computed_attributes.keys
end

def primary_key
Expand Down Expand Up @@ -89,7 +89,7 @@ def query(name, lambda=nil, &block)
raise "Query #{name} already exists" if queries.key? name

queries[name] = lambda || block

define_method name do |*args|
default_query.instance_exec(*args, &self.class.queries.fetch(name))
end
Expand Down Expand Up @@ -210,21 +210,21 @@ def dataset
def qualified_collection_name
data_source.qualify self.class.collection_name
end

def qualify(collection_name, data_source_name: nil)
data_source_name ||= self.class.data_source_name
environment.qualify data_source_name, collection_name
end

def default_query
Query.new collection_class: self.class,
dataset: dataset.select_all(self.class.collection_name),
Query.new collection_class: self.class,
dataset: dataset.select_all(self.class.collection_name),
environment: environment
end

def build_query(filter=nil, &block)
raise ArgumentError, 'must specify filter hash or block' if filter.nil? && block.nil?

if filter
default_query.where(filter)
else
Expand All @@ -233,7 +233,7 @@ def build_query(filter=nil, &block)
end

def transform_attributes_to_db(attributes)
attributes.each_with_object({}) do |(attribute_name, value), result|
attributes.each_with_object({}) do |(attribute_name, value), result|
transformed_value = Rasti::DB.to_db data_source.db, qualified_collection_name, attribute_name, value
result[attribute_name] = transformed_value
end
Expand All @@ -247,7 +247,7 @@ def split_related_attributes(attributes)

[collection_attributes, relations_ids]
end

def save_relations(primary_key, relations_primary_keys)
relations_primary_keys.each do |relation_name, relation_primary_keys|
relation = self.class.relations[relation_name]
Expand Down Expand Up @@ -281,9 +281,9 @@ def insert_relation_table(relation, primary_key, relation_primary_keys)
relation_data_source = environment.data_source relation.relation_data_source_name
relation_collection_name = relation_data_source.qualify relation.relation_collection_name

values = relation_primary_keys.map do |relation_pk|
values = relation_primary_keys.map do |relation_pk|
{
relation.source_foreign_key => primary_key,
relation.source_foreign_key => primary_key,
relation.target_foreign_key => relation_pk
}
end
Expand Down
2 changes: 1 addition & 1 deletion lib/rasti/db/data_source.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def initialize(db, schema=nil)
def qualify(collection_name)
schema ? Sequel[schema][collection_name] : Sequel[collection_name]
end

end
end
end
107 changes: 3 additions & 104 deletions lib/rasti/db/model.rb
Original file line number Diff line number Diff line change
@@ -1,112 +1,11 @@
module Rasti
module DB
class Model
class Model < Rasti::Model

class UninitializedAttributeError < StandardError

attr_reader :attribute

def initialize(attribute)
@attribute = attribute
super "Uninitialized attribute #{attribute}"
end

end


class << self

def [](*attribute_names)
Class.new(self) do
attribute(*attribute_names)

def self.inherited(subclass)
subclass.instance_variable_set :@attributes, attributes.dup
end
end
end

def attributes
@attributes ||= []
end

def model_name
name || self.superclass.name
end

def to_s
"#{model_name}[#{attributes.join(', ')}]"
end
alias_method :inspect, :to_s

private

def attribute(*names)
names.each do |name|
raise ArgumentError, "Attribute #{name} already exists" if attributes.include?(name)

attributes << name

define_method name do
fetch_attribute name
end
end
end

end


def initialize(attributes)
invalid_attributes = attributes.keys - self.class.attributes
raise "#{self.class.model_name} invalid attributes: #{invalid_attributes.join(', ')}" unless invalid_attributes.empty?
@attributes = attributes
end

def merge(new_attributes)
self.class.new attributes.merge(new_attributes)
end

def eql?(other)
instance_of?(other.class) && to_h.eql?(other.to_h)
end

def ==(other)
other.kind_of?(self.class) && to_h == other.to_h
end

def hash
attributes.map(&:hash).hash
end

def to_s
"#<#{self.class.model_name}[#{attributes.map { |n,v| "#{n}: #{v.inspect}" }.join(', ')}]>"
end
alias_method :inspect, :to_s

def to_h
self.class.attributes.each_with_object({}) do |name, hash|
if attributes.key? name
value = fetch_attribute name
case value
when Model
hash[name] = value.to_h
when Array
hash[name] = value.map do |e|
e.is_a?(Model) ? e.to_h : e
end
else
hash[name] = value
end
end
end
end

private

attr_reader :attributes

def fetch_attribute(name)
attributes.key?(name) ? Rasti::DB.from_db(attributes[name]) : raise(UninitializedAttributeError, name)
def cast_attribute(type, value)
super type, Rasti::DB.from_db(value)
end

end
Expand Down
2 changes: 1 addition & 1 deletion lib/rasti/db/nql/nodes/binary_node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module DB
module NQL
module Nodes
class BinaryNode < Treetop::Runtime::SyntaxNode

def dependency_tables
values.flat_map(&:dependency_tables)
end
Expand Down
2 changes: 1 addition & 1 deletion lib/rasti/db/nql/nodes/parenthesis_sentence.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module DB
module NQL
module Nodes
class ParenthesisSentence < Treetop::Runtime::SyntaxNode

def dependency_tables
sentence.dependency_tables
end
Expand Down
6 changes: 3 additions & 3 deletions lib/rasti/db/relations/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module Rasti
module DB
module Relations
class Base

include Sequel::Inflections

attr_reader :name, :source_collection_class
Expand Down Expand Up @@ -63,10 +63,10 @@ def with_prefix(prefix, name)

def validate_join!
if source_collection_class.data_source_name != target_collection_class.data_source_name
raise "Invalid join of multiple data sources: #{source_collection_class.data_source_name}.#{source_collection_class.collection_name} > #{target_collection_class.data_source_name}.#{target_collection_class.collection_name}"
raise "Invalid join of multiple data sources: #{source_collection_class.data_source_name}.#{source_collection_class.collection_name} > #{target_collection_class.data_source_name}.#{target_collection_class.collection_name}"
end
end

end
end
end
Expand Down
30 changes: 15 additions & 15 deletions lib/rasti/db/relations/graph.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,23 @@ class Graph
def initialize(environment, collection_class, relations=[], selected_attributes={}, excluded_attributes={})
@environment = environment
@collection_class = collection_class
@graph = build_graph relations,
Hash::Indifferent.new(selected_attributes),
@graph = build_graph relations,
Hash::Indifferent.new(selected_attributes),
Hash::Indifferent.new(excluded_attributes)
end

def merge(relations:[], selected_attributes:{}, excluded_attributes:{})
Graph.new environment,
collection_class,
(flat_relations | relations),
Graph.new environment,
collection_class,
(flat_relations | relations),
flat_selected_attributes.merge(selected_attributes),
flat_excluded_attributes.merge(excluded_attributes)
end

def with_all_attributes_for(relations)
relations_with_all_attributes = relations.map { |r| [r, nil] }.to_h

merge selected_attributes: relations_with_all_attributes,
merge selected_attributes: relations_with_all_attributes,
excluded_attributes: relations_with_all_attributes
end

Expand All @@ -37,7 +37,7 @@ def fetch_graph(rows)

graph.roots.each do |node|
relation_of(node).fetch_graph environment,
rows,
rows,
node[:selected_attributes],
node[:excluded_attributes] ,
subgraph_of(node)
Expand Down Expand Up @@ -88,25 +88,25 @@ def subgraph_of(node)
excluded[id] = descendant[:excluded_attributes]
end

Graph.new environment,
relation_of(node).target_collection_class,
relations,
selected,
Graph.new environment,
relation_of(node).target_collection_class,
relations,
selected,
excluded
end

def build_graph(relations, selected_attributes, excluded_attributes)
HierarchicalGraph.new.tap do |graph|
flatten(relations).each do |relation|
flatten(relations).each do |relation|
sections = relation.split('.')

graph.add_node relation, name: sections.last.to_sym,
selected_attributes: selected_attributes[relation],
excluded_attributes: excluded_attributes[relation]

if sections.count > 1
parent_id = sections[0..-2].join('.')
graph.add_relation parent_id: parent_id,
graph.add_relation parent_id: parent_id,
child_id: relation
end
end
Expand Down
8 changes: 4 additions & 4 deletions lib/rasti/db/relations/many_to_many.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,19 @@ def fetch_graph(environment, rows, selected_attributes=nil, excluded_attributes=

relations_graph.fetch_graph join_rows if relations_graph

relation_rows = join_rows.each_with_object(Hash.new { |h,k| h[k] = [] }) do |row, hash|
attributes = row.select { |attr,_| target_collection_class.model.attributes.include? attr }
relation_rows = join_rows.each_with_object(Hash.new { |h,k| h[k] = [] }) do |row, hash|
attributes = row.select { |attr,_| target_collection_class.collection_attributes.include? attr }
hash[row[:source_foreign_key]] << target_collection_class.model.new(attributes)
end

rows.each do |row|
rows.each do |row|
row[name] = relation_rows.fetch row[source_collection_class.primary_key], []
end
end

def add_join(environment, dataset, prefix=nil)
validate_join!

many_to_many_relation_alias = with_prefix prefix, "#{relation_collection_name}_#{SecureRandom.base64}"

relation_name = prefix ? Sequel[prefix] : Sequel[source_collection_class.collection_name]
Expand Down
8 changes: 4 additions & 4 deletions lib/rasti/db/relations/many_to_one.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,18 @@ def fetch_graph(environment, rows, selected_attributes=nil, excluded_attributes=
query = query.select_attributes(*selected_attributes) if selected_attributes
query = relations_graph.apply_to query if relations_graph

relation_rows = query.each_with_object({}) do |row, hash|
relation_rows = query.each_with_object({}) do |row, hash|
hash[row.public_send(source_collection_class.primary_key)] = row
end
rows.each do |row|

rows.each do |row|
row[name] = relation_rows[row[foreign_key]]
end
end

def add_join(environment, dataset, prefix=nil)
validate_join!

relation_alias = join_relation_name prefix

relation_name = prefix ? Sequel[prefix] : Sequel[source_collection_class.collection_name]
Expand Down
Loading

0 comments on commit 7c96610

Please sign in to comment.