Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor resources #8

Open
wants to merge 2 commits into
base: rewrite
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed src/blender_farm/blender_farm-0.1.0.gem
Binary file not shown.
4 changes: 3 additions & 1 deletion src/blender_farm/lib/blender_farm.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
require "blender_farm/version"
require "blender_farm/resources/blend"
require "blender_farm/resources/user"
require "blender_farm/resources/project"
require "blender_farm/resources/blend"
require "blender_farm/resources/job"
require "logger"
require "aws-sdk-dynamodb"

Expand Down
163 changes: 64 additions & 99 deletions src/blender_farm/lib/blender_farm/dynamo_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,6 @@ module BlenderFarm
module DynamoResource
extend ActiveSupport::Concern

def self.instance_from_item(item)
including_classes = ObjectSpace.each_object(Class).select do |c|
c.included_modules.include?(self)
end

item = item.with_indifferent_access
parsed_key = parse_key(item)

klass = including_classes.find do |k|
p k.key_attributes
[:hk, :rk].all? do |key_name|
parsed_key[key_name].keys.map(&:to_sym) == k.key_attributes[key_name]
end
end

klass.instance_from_item(item)
end

# Given hk and rk with hash-delimited values e.g. "project_id#1234#blend_id#1234"
# return hash of form {"project_id" => 1234, "blend_id" => 1234}
def self.parse_key(item)
Expand All @@ -34,106 +16,89 @@ def self.parse_key(item)
].with_indifferent_access
end

def self.all_for_user(user_id)
all_items = dynamo_client.query(
table_name: BlenderFarm.config[:table],
key_condition_expression: "hk = :hk",
expression_attribute_values: {
":hk" => "user_id##{user_id}"
}
).items

all_items.map{|item| DynamoResource.instance_from_item(item) }
end

# Get the composite key based on key attributes of object
def key
self.class.build_key(**key_attributes)
end

# Attributes that comprise the composite key for the resource
def key_attributes
Hash[
self.class.key_attributes.values.flatten.map do |attr_name|
[attr_name, self.send(attr_name)]
end
]
self.class.generate_key(**key_params)
end

def get_hierarchy
descendents = self.class.get_hierarchy(**key_attributes).reject do |item|
item["hk"] == self.key["hk"] && item["rk"] == self.key["rk"]
end
# Write the model to the table, updating if a match is found.
def put
db_attributes = self.class.db_attributes.map do |attr_name|
[attr_name, self.send(attr_name)]
end.to_h

descendents.map{|item| DynamoResource.instance_from_item(item) }
BlenderFarm.dynamo_client.put_item(
table_name: BlenderFarm.config[:table],
item: self.key.merge(db_attributes)
)
end

class_methods do
# Use the resource's key_attributes to generate
# a composite key with the provided parameters
def build_key(**key_params)
Hash[
key_attributes.map do |key_name, key_attrs|
key_pairs = key_attrs.map do |attr|
[attr.to_s, key_params[attr]].join("#")
end
[ key_name, key_pairs.join("#") ]
end
].with_indifferent_access
# Attributes to store in the table that aren't encoded in the key
# acts as getter and setter
def db_attribute(*attrs)
@db_attributes ||= []
attr_accessor(*attrs)
@db_attributes = @db_attributes.push(*attrs).uniq
end
alias_method :db_attributes, :db_attribute

def dynamo_client
@dynamo_client = BlenderFarm.dynamo_client
# Using the KEY_TEMPLATE and key_params, generate a composite key
def generate_key(**key_params)
Hash[
self::KEY_TEMPLATE.map{|k,template| [k, template % key_params] }
].with_indifferent_access
end

def get_dynamo_item(**key_params)
composite_key = build_key(**key_params)
dynamo_client.get_item(
def find(**key_params)
key = generate_key(key_params)
eav = key.transform_keys{|k| ":#{k}"}
items = BlenderFarm.dynamo_client.query(
table_name: BlenderFarm.config[:table],
key: composite_key
).item&.with_indifferent_access
end

def id_name
self.name.demodulize.downcase + "_id"
end
key_condition_expression: "hk = :hk AND begins_with(rk, :rk)",
expression_attribute_values: eav
).items

def create(**create_params)
if create_params[id_name.to_sym].nil?
create_params[id_name.to_sym] = SecureRandom.uuid
end
key = build_key(**create_params)
key_params = DynamoResource.parse_key(key).values.reduce(:merge)
item_attrs = key.merge(create_params)
item_without_key_attrs = item_attrs.except(*key_params.keys)
dynamo_client.put_item(table_name: BlenderFarm.config[:table], item: item_without_key_attrs)
instance_from_item(item_attrs.symbolize_keys)
# items.each do |item|
# params = item["rk"].split("#").each_cons(2).to_a
# model_name = params.last.first
# klass = "BlenderFarm::Resources::#{model_name.camelize}".constantize
# model_name.camelize
# end
end

def instance_from_item(item)
item = item.with_indifferent_access
key_params = DynamoResource.parse_key(item).values.reduce(:merge)
all_attributes = item.merge(key_params).except(:hk, :rk)

new(**all_attributes.symbolize_keys)
def create(**params)
instance = Helpers.build_hierarchy(self, params)
instance.put
instance
end
end

# Accepts the resource params that comprise the key of the item
# Returns an instance of the resource that matched the params
def find(**key_params)
dynamo_item = get_dynamo_item(**key_params)
return nil if dynamo_item.nil?
instance_from_item(dynamo_item)
module Helpers
# Given a class and hash, build the hierarchy of models from this class upward
# returns and instance of the specified class, with associated models built as well.
#
# For example: build_hierarchy(Blend, {id: "blendID", project: {id: "projectID", user: {id: "userID"}}})
def self.build_hierarchy(klass, hash)
assoc, attrs = hash.partition do |k,v|
v.is_a?(Hash) && BlenderFarm::Resources.const_defined?(k.to_s.camelize)
end.map(&:to_h)

instance = klass.new(**attrs)
klass_name_as_attr = klass.name.demodulize.downcase.underscore

# For each association provided in hash, recursively instantiate and associate models.
assoc.each do |k,v|
assoc_klass = "BlenderFarm::Resources::#{k.to_s.camelize}".constantize
assoc_instance = build_hierarchy(assoc_klass, v)
[:"add_#{klass_name_as_attr}", :"#{klass_name_as_attr}="].find do |setter|
assoc_instance.send(setter, instance)
end
end
instance
end

# Get the resource and all descendents
def get_hierarchy(**key_params)
eav = build_key(**key_params).transform_keys{|k| ":#{k}" } #Build query expression values from key
dynamo_client.query(
table_name: BlenderFarm.config[:table],
key_condition_expression: "hk = :hk AND begins_with(rk, :rk)",
expression_attribute_values: eav
).items
def self.db_item_to_instance(item)

end
end
end
Expand Down
29 changes: 20 additions & 9 deletions src/blender_farm/lib/blender_farm/resources/blend.rb
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
require "blender_farm/dynamo_resource"
require 'securerandom'

module BlenderFarm
module Resources
class Blend
include DynamoResource
attr_accessor :id, :project, :jobs
db_attribute :name

attr_accessor :user_id, :project_id, :blend_id, :name, :project, :jobs
KEY_TEMPLATE = {
hk: "user#%{user}",
rk: "project#%{project}#blend#%{blend}"
}

def self.key_attributes
def initialize(id: SecureRandom.uuid, name: nil, jobs: [])
@id = id
@name = name
@jobs = jobs
end

def key_params
{
hk: [:user_id],
rk: [:project_id, :blend_id]
project: project.id,
user: project.user.id,
blend: id
}
end

def initialize(user_id:, project_id:, blend_id:, name:)
@user_id = user_id
@project_id = project_id
@blend_id = blend_id
@name = name
def add_job(job)
@jobs << job
job.blend = self
end
end
end
Expand Down
37 changes: 17 additions & 20 deletions src/blender_farm/lib/blender_farm/resources/job.rb
Original file line number Diff line number Diff line change
@@ -1,33 +1,30 @@
require "blender_farm/dynamo_resource"
require 'securerandom'

module BlenderFarm
module Resources
class Job
include DynamoResource
attr_accessor :id, :blend
db_attribute :type

attr_accessor :user_id, :project_id, :blend_id, :job_id, :type
attr_writer :project, :blend
KEY_TEMPLATE = {
hk: "user#%{user}",
rk: "project#%{project}#blend#%{blend}#job#%{job}"
}

def self.key_attributes
{
hk: [:user_id],
rk: [:project_id, :blend_id, :job_id]
}
end

def initialize(user_id:, project_id:, blend_id:, type:)
@user_id = user_id
@project_id = project_id
@blend_id = blend_id
@name = type
def initialize(id: SecureRandom.uuid, type: nil)
@id = id
@type = type
end

def project
blend.project
end

def blend
@blend ||= Blend.find(user_id: user_id, project_id: project_id, blend_id: blend_id)
def key_params
{
project: blend.project.id,
user: blend.project.user.id,
blend: blend.id,
job: id
}
end
end
end
Expand Down
27 changes: 16 additions & 11 deletions src/blender_farm/lib/blender_farm/resources/project.rb
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
require "blender_farm/dynamo_resource"
require 'securerandom'

module BlenderFarm
module Resources
class Project
include DynamoResource
attr_accessor :id, :user, :blends
db_attribute :name

attr_accessor :user_id, :project_id, :name, :blends
KEY_TEMPLATE = {
hk: "user#%{user}",
rk: "project#%{project}"
}

def self.key_attributes
{
hk: [:user_id],
rk: [:project_id]
}
def initialize(id: SecureRandom.uuid, name: nil, blends: [])
@id = id
@name = name
@blends = blends
end

def initialize(user_id:, project_id:, name:)
@user_id = user_id
@project_id = project_id
@name = name
@blends = []
def key_params
{
project: id,
user: user.id
}
end

def add_blend(blend)
Expand Down
30 changes: 30 additions & 0 deletions src/blender_farm/lib/blender_farm/resources/user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
require "blender_farm/dynamo_resource"
require 'securerandom'

module BlenderFarm
module Resources
class User
include DynamoResource
attr_accessor :id, :projects

KEY_TEMPLATE = {
hk: "user#%{user}"
}

def initialize(id: SecureRandom.uuid, projects: [])
@id = id
@projects = projects
end

def key_params
{ user: id }
end

def add_project(project)
@projects << project
project.user = self
end
end
end
end