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

Updated automatic lookup of representers for entities #73

Closed
wants to merge 2 commits into from
Closed
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
66 changes: 54 additions & 12 deletions lib/roar/rails/formats.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,18 @@ def name_for(format, model, controller_path) # DISCUSS: should we pass and proce
end

def collection_representer(format, model, controller_path)
infer_representer(controller_path)
add_representer_suffix(controller_path).camelize.constantize
end

def entity_representer(format, model, controller_path)
model_name = model.class.name.underscore
representer_name = add_representer_suffix(
model.class.name.underscore
)

if namespace = controller_path.namespace
model_name = "#{namespace}/#{model_name}"
end

infer_representer(model_name)
end

def infer_representer(model_name)
add_representer_suffix(model_name).camelize.constantize
find_namespaced_class(
controller_path.camelize,
representer_name.camelize
)
end

def add_representer_suffix(prefix)
Expand All @@ -64,11 +61,56 @@ def detect_collection(model)
return true if Object.const_defined?("ActiveRecord") and model.kind_of?(ActiveRecord::Relation)
end

def find_namespaced_class(basis, to_find)
# If the class we're looking for starts with :: then just use that
return to_find.constantize if to_find =~ /^::/

ancestor_mods = build_ancestor_modules(basis)
namespace = find_class_in_namespaces(ancestor_mods, to_find)

namespace.nil? ? to_find.constantize : get_constant(namespace, to_find)
end

def build_ancestor_modules(basis)
namespaces = []
ancestors = basis.split('::')

if ancestors.size > 1
# Transform any namespace this class has into a module
receiver = Object
namespaces = ancestors[0..-2].map do |mod|
receiver = receiver.const_get(mod)
end
end

namespaces
end

def find_class_in_namespaces(modules, class_name)
modules.reverse.detect do |ns|
get_constant(ns, class_name)
end
end

# Used to patch different behavior between
# different versions of ruby when looking up a
# constant like V1::Singer
def get_constant(basis, class_string)
begin
class_string.split('::').reduce(basis) do |basis, const|
basis.const_get(const, false)
end
rescue NameError
nil
end

end

class Path < String
def namespace
return unless ns = self.match(/(.+)\/\w+$/)
ns[1]
end
end
end
end
end
53 changes: 46 additions & 7 deletions test/formats_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,34 @@ module ObjectsRepresenter
end

module V1
module SingerRepresenter
end
module BassistRepresenter
end
module SingersRepresenter
Singer = Class.new

SingerRepresenter = Class.new
BassistRepresenter = Class.new
SingersRepresenter = Class.new

module Inner
SingerRepresenter = Class.new
end
end

class Bassist
module V2
Singer = Class.new
SingerRepresenter = Class.new
SingersRepresenter = Class.new
end

module Inner
Singer = Class.new
end

module Outer
Singer = Class.new
SingerRepresenter = Class.new
end

Bassist = Class.new

class FormatsTest < MiniTest::Spec
let (:subject) { Roar::Rails::Formats.new }

Expand Down Expand Up @@ -93,6 +110,28 @@ class FormatsTest < MiniTest::Spec
subject.for(:json, [Object.new], "v1/singers").must_equal V1::SingersRepresenter
end
end

describe "namespaced class" do
it "returns a namespaced entity" do
subject.for(:json, V1::Singer.new, 'v1/singers').must_equal V1::SingerRepresenter
end

it 'finds the right class in another namespace' do
subject.for(:json, V2::Singer.new, 'v1/singers').must_equal V2::SingerRepresenter
end

it 'finds the right class in an inner namespace' do
subject.for(:json, Inner::Singer.new, 'v1/singers').must_equal V1::Inner::SingerRepresenter
end

it 'finds the right class from the root namespace' do
subject.for(:json, Outer::Singer.new, 'v1/singers').must_equal Outer::SingerRepresenter
end

it 'finds the right class in a deep namespace' do
subject.for(:json, Singer.new, 'v1/inner/singers').must_equal V1::Inner::SingerRepresenter
end
end
end

describe "with ActiveRecord::Relation" do
Expand Down Expand Up @@ -135,4 +174,4 @@ class PathTest < MiniTest::Spec
it { path.new("bands").namespace.must_equal nil }
it { path.new("v1/bands").namespace.must_equal "v1" }
it { path.new("api/v1/bands").namespace.must_equal "api/v1" }
end
end