Skip to content

Commit

Permalink
Merge pull request #25 from AMHOL/feature/nazrin-struct
Browse files Browse the repository at this point in the history
Add Nazrin::DataAccessor::Struct
  • Loading branch information
tsuwatch committed Jan 30, 2018
2 parents 3fed7ca + d094df3 commit cb58c36
Show file tree
Hide file tree
Showing 8 changed files with 296 additions and 21 deletions.
37 changes: 24 additions & 13 deletions lib/nazrin/data_accessor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ def register_accessor(clazz)

def accessor_for(clazz)
return nil if clazz.name.nil? || clazz.name.empty?
if defined?(::ActiveRecord::Base) && clazz.ancestors.include?(::ActiveRecord::Base)

if clazz.respond_to?(:nazrin_searchable_config) && clazz.nazrin_searchable_config.domain_name
require 'nazrin/data_accessor/struct'
return Nazrin::DataAccessor::Struct[clazz.nazrin_searchable_config]
elsif defined?(::ActiveRecord::Base) && clazz.ancestors.include?(::ActiveRecord::Base)
require 'nazrin/data_accessor/active_record'
return Nazrin::DataAccessor::ActiveRecord
elsif defined?(::Mongoid::Document) && clazz.ancestors.include?(::Mongoid::Document)
Expand All @@ -45,26 +49,29 @@ def accessors
end
end

attr_reader :model
attr_reader :options

def initialize(model, options)
@model = model
@options = options
end

def results(client)
@client = client

res = @client.search
collection = load_all(res.data.hits.hit.map(&:id))
res = client.search
collection = load_all(data_from_response(res))
start = client.parameters[:start]
size = client.parameters[:size]

if @client.parameters[:size] && @client.parameters[:start]
if size && start
total_count = res.data.hits.found

Nazrin::PaginationGenerator.generate(
collection,
current_page: current_page,
per_page: @client.parameters[:size],
current_page: current_page(start, size),
per_page: size,
total_count: total_count,
last_page: last_page(total_count))
last_page: last_page(size, total_count))
else
collection
end
Expand All @@ -74,14 +81,18 @@ def load_all
raise NotImplementedError
end

def data_from_response
raise NotImplementedError
end

private

def last_page(total_count)
(total_count / @client.parameters[:size].to_f).ceil
def last_page(size, total_count)
(total_count / size.to_f).ceil
end

def current_page
(@client.parameters[:start] / @client.parameters[:size].to_f).ceil + 1
def current_page(start, size)
(start / size.to_f).ceil + 1
end
end
end
10 changes: 7 additions & 3 deletions lib/nazrin/data_accessor/active_record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@ class ActiveRecord < Nazrin::DataAccessor
# load from activerecord
def load_all(ids)
records_table = {}
@options.each do |k, v|
@model = @model.send(k, v)
options.each do |k, v|
model = model.send(k, v)
end
@model.where(id: ids).each do |record|
model.where(id: ids).each do |record|
records_table[record.id] = record
end
ids.map do |id|
records_table.select { |k, _| k == id.to_i }[id.to_i]
end.reject(&:nil?)
end

def data_from_response(res)
res.data.hits.hit.map(&:id)
end
end
end
end
14 changes: 9 additions & 5 deletions lib/nazrin/data_accessor/mongoid.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,24 @@ class DataAccessor
class Mongoid < Nazrin::DataAccessor
def load_all(ids)
documents_table = {}
@options.each do |k, v|
@model = if v.nil?
@model.send(k)
options.each do |k, v|
model = if v.nil?
model.send(k)
else
@model.send(k, v)
model.send(k, v)
end
end
@model.where('_id' => { '$in' => ids }).each do |document|
model.where('_id' => { '$in' => ids }).each do |document|
documents_table[document._id.to_s] = document
end
ids.map do |id|
documents_table[id]
end.reject(&:nil?)
end

def data_from_response(res)
res.data.hits.hit.map(&:id)
end
end
end
end
68 changes: 68 additions & 0 deletions lib/nazrin/data_accessor/struct.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
require 'aws-sdk-cloudsearch'

module Nazrin
class DataAccessor
class Struct < Nazrin::DataAccessor
class MissingDomainNameConfigError < StandardError; end

class << self
attr_reader :config

def [](config)
Class.new(self).tap do |clazz|
clazz.instance_variable_set(:@config, config)
end
end

def transform_attributes(attributes)
attributes.each_with_object({}) do |(name, value), hash|
type = field_types[name]

if type.end_with?('array')
hash[name] = value
else
hash[name] = value.first
end
end
end

def field_types
return @field_types if defined?(@field_types)

response = cloudsearch_client.describe_index_fields(
domain_name: config.domain_name
)

@field_types = response.index_fields.each_with_object({}) do |field, fields|
name = field.options[:index_field_name]
type = field.options[:index_field_type]

fields[name] = type
end
end

private

def cloudsearch_client
@cloudsearch_client ||= Aws::CloudSearch::Client.new(
access_key_id: config.access_key_id,
secret_access_key: config.secret_access_key,
logger: config.logger
)
end
end

def load_all(data)
data.map do |attributes|
model.new(attributes)
end
end

def data_from_response(res)
res.data[:hits][:hit].map do |hit|
self.class.transform_attributes(hit[:fields])
end
end
end
end
end
2 changes: 2 additions & 0 deletions lib/nazrin/searchable/config.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module Nazrin
module Searchable
class Configuration
attr_accessor :domain_name

%i(
search_endpoint
document_endpoint
Expand Down
1 change: 1 addition & 0 deletions nazrin.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Gem::Specification.new do |spec|
spec.require_paths = ['lib']

spec.add_dependency 'aws-sdk-core', '~> 3'
spec.add_dependency 'aws-sdk-cloudsearch', '~> 1.0'
spec.add_dependency 'aws-sdk-cloudsearchdomain', '~> 1.0'
spec.add_dependency 'activesupport', '>= 4.0.0'

Expand Down
110 changes: 110 additions & 0 deletions spec/nazrin/data_accessor/struct_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
require 'spec_helper'

describe Nazrin::DataAccessor::Struct do
let(:clazz) do
Class.new do
def self.name; 'CustomStruct'; end
include Nazrin::Searchable

attr_reader :attributes

searchable_configure do |config|
config.domain_name = 'my-domain-name'
end

def initialize(attributes)
@attributes = attributes
end
end
end
let(:data_accessor) { Nazrin::DataAccessor.for(clazz) }

it do
expect(data_accessor).to be < Nazrin::DataAccessor::Struct
end

describe '.field_types' do
let(:cs_client) do
instance_double(Aws::CloudSearch::Client)
end
let(:index_fields_response) { double(index_fields: index_fields) }
let(:index_fields) do
[id_field, name_field, tags_field]
end
let(:id_field) do
double(
options: {
index_field_name: 'id',
index_field_type: 'int'
}
)
end
let(:name_field) do
double(
options: {
index_field_name: 'name',
index_field_type: 'text'
}
)
end
let(:tags_field) do
double(
options: {
index_field_name: 'tags',
index_field_type: 'literal-array'
}
)
end

before do
allow(Aws::CloudSearch::Client).to receive(:new).and_return(
cs_client
)
allow(cs_client).to receive(:describe_index_fields).and_return(
index_fields_response
)
end

it do
expect(Aws::CloudSearch::Client).to receive(:new).with(
access_key_id: Nazrin.config.access_key_id,
secret_access_key: Nazrin.config.secret_access_key,
logger: Nazrin.config.logger
)
expect(cs_client).to receive(:describe_index_fields).with(
domain_name: 'my-domain-name'
)
expect(data_accessor.field_types).to eq(
'id' => 'int',
'name' => 'text',
'tags' => 'literal-array'
)
end
end

describe '.transform_attributes' do
let(:attributes) do
{
'id' => ['1'],
'name' => ['Michael'],
'tags' => ['one', 'two', 'three']
}
end

before do
allow(data_accessor).to receive(:field_types).and_return(
'id' => 'int',
'name' => 'text',
'tags' => 'literal-array'
)
end

it do
expect(data_accessor.transform_attributes(attributes)).to eq(
'id' => '1',
'name' => 'Michael',
'tags' => ['one', 'two', 'three']
)
end
end
end
Loading

0 comments on commit cb58c36

Please sign in to comment.