Skip to content

Commit e00b802

Browse files
Better Lookup to support namespaces. (#27)
* added test, begin adding lookup class * now supporting namespaces
1 parent a88a827 commit e00b802

File tree

19 files changed

+308
-129
lines changed

19 files changed

+308
-129
lines changed

.rubocop.yml

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
AllCops:
2+
TargetRubyVersion: 2.3
23
Exclude:
34
- config/initializers/forbidden_yaml.rb
45
- !ruby/regexp /(vendor|bundle|bin|db|tmp)\/.*/

lib/skinny_controllers.rb

+4-1
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,19 @@
55
require 'active_support/core_ext/object/blank'
66
require 'active_support/core_ext/object/try'
77
require 'active_support/core_ext/string/inflections'
8+
require 'active_support/core_ext/module/delegation'
9+
810

911
# files for this gem
1012
require 'skinny_controllers/default_verbs'
1113
require 'skinny_controllers/exceptions'
1214
require 'skinny_controllers/logging'
1315
require 'skinny_controllers/lookup/namespace'
14-
require 'skinny_controllers/lookup/controller'
1516
require 'skinny_controllers/lookup/model'
1617
require 'skinny_controllers/lookup/operation'
1718
require 'skinny_controllers/lookup/policy'
19+
require 'skinny_controllers/lookup/ensure_existence'
20+
require 'skinny_controllers/lookup'
1821
require 'skinny_controllers/policy/base'
1922
require 'skinny_controllers/policy/default'
2023
require 'skinny_controllers/policy/deny_all'

lib/skinny_controllers/diet.rb

+12-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ def operation
1717
@operation ||= operation_class.new(
1818
current_user,
1919
params, params_for_action,
20-
action_name, self.class.model_key
20+
action_name, self.class.model_key,
21+
_lookup
2122
)
2223
end
2324

@@ -26,7 +27,15 @@ def operation
2627
# @example SomeObjectsController => Operation::SomeObject::Action
2728
# @return [Class] the operation class for the model and verb
2829
def operation_class
29-
Lookup::Operation.from_controller(self.class.name, verb_for_action, self.class.model_class)
30+
_lookup.operation_class
31+
end
32+
33+
def _lookup
34+
@_lookup ||= Lookup.from_controller(
35+
controller_class: self.class,
36+
verb: verb_for_action,
37+
model_class: self.class.model_class
38+
)
3039
end
3140

3241
# abstraction for `operation.run`
@@ -63,7 +72,7 @@ def params_for_action
6372
elsif klass
6473
klass.name.underscore
6574
else
66-
Lookup::Controller.model_name(self.class.name).underscore
75+
_lookup.model_name.underscore
6776
end
6877

6978
action_params_method = "#{action_name}_#{model_key}_params"

lib/skinny_controllers/lookup.rb

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# frozen_string_literal: true
2+
module SkinnyControllers
3+
# This class provides a way to determine all names / classes of operations
4+
# and policies based on he given information.
5+
#
6+
# The class methods show what is required for each scenario
7+
class Lookup
8+
include EnsureExistence
9+
include Policy
10+
11+
class << self
12+
def from_controller(controller_class:, verb:, model_class:)
13+
Lookup.new(
14+
controller_class_name: controller_class.name,
15+
verb: verb,
16+
model_class: model_class
17+
)
18+
end
19+
20+
def from_operation(operation_class:)
21+
qualified_name = operation_class.name
22+
parts = qualified_name.split('::')
23+
operation_name = parts[-2]
24+
operation_parts = operation_name.split(SkinnyControllers.operations_suffix)
25+
26+
Lookup.new(
27+
verb: parts.last,
28+
# namespace: parts[0..-3],
29+
operation_name: operation_name,
30+
operation_class: operation_class,
31+
model_name: operation_parts.first,
32+
namespace: qualified_name.deconstantize.deconstantize
33+
)
34+
end
35+
end
36+
37+
# @param [Hash] args - all possible parameters
38+
# - @option [String] controller_class_name
39+
# - @option [Class] controller_class
40+
# - @option [String] verb
41+
# - @option [String] operation_name
42+
# - @option [String] model_name
43+
# - @option [Class] model_class
44+
# - @option [Hash] options
45+
def initialize(args = {})
46+
@controller_class_name = args[:controller_class_name]
47+
@controller_class = args[:controller_class]
48+
@verb_for_action = args[:verb]
49+
@operation_name = args[:operation_name]
50+
@operation_class = args[:operation_class]
51+
@model_name = args[:model_name]
52+
@namespace = args[:namespace]
53+
@model_class = args[:model_class]
54+
@policy_method_name = args[:policy_method_name]
55+
@options = args[:options] || {}
56+
end
57+
58+
# PostsController
59+
# => PostOperations::Verb
60+
#
61+
# Api::V2::PostsController
62+
# => Api::V2::PostOperations::Verb
63+
def operation_class
64+
@operation_class ||= begin
65+
ensure_namespace!(operation_namespace) if operation_namespace.present?
66+
ensure_operation_class!(namespaced_operation_name)
67+
end
68+
end
69+
70+
def policy_class
71+
@policy_class ||= begin
72+
ensure_namespace!(namespace) if namespace.present?
73+
ensure_policy_class!(namespaced_policy_name)
74+
end
75+
end
76+
77+
def policy_method_name
78+
@policy_method_name ||= @verb_for_action.underscore + POLICY_METHOD_SUFFIX
79+
end
80+
81+
def model_class
82+
@model_class ||= @options[:model_class] || model_name.safe_constantize
83+
end
84+
85+
def model_name
86+
@model_name ||= @model_class.try(:name) || resource_parts[-1].singularize
87+
end
88+
89+
# @return [String] name of the supposed operation class
90+
def namespaced_operation_name
91+
@namespaced_operation_name ||= [
92+
operation_namespace,
93+
@verb_for_action
94+
].reject(&:blank?).join('::')
95+
end
96+
97+
def namespaced_policy_name
98+
@namespaced_policy_name ||= [
99+
namespace,
100+
"#{model_name}#{SkinnyControllers.policy_suffix}"
101+
].reject(&:blank?).join('::')
102+
end
103+
104+
def operation_namespace
105+
@operation_namespace ||= [
106+
namespace,
107+
operation_name
108+
].reject(&:blank?).join('::')
109+
end
110+
111+
def operation_name
112+
@operation_name ||= "#{model_name}#{SkinnyControllers.operations_suffix}"
113+
end
114+
115+
# @return [String] the namespace
116+
def namespace
117+
@namespace ||= begin
118+
resource_parts.length > 2 ? resource_parts[0..-2].join('::') : ''
119+
end
120+
end
121+
122+
# PostsController
123+
# => Posts
124+
#
125+
# Api::V2::PostsController
126+
# => Api, V2, Posts
127+
def resource_parts
128+
@resource_parts ||= controller_class_name.split(/::|Controller/)
129+
end
130+
131+
def controller_class_name
132+
@controller_class_name ||= @controller_class.name
133+
end
134+
end
135+
end

lib/skinny_controllers/lookup/controller.rb

-39
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
module SkinnyControllers
2+
class Lookup
3+
module EnsureExistence
4+
module_function
5+
6+
# @return [Module] namespace
7+
def ensure_namespace!(namespace)
8+
Namespace.create_namespace(namespace)
9+
end
10+
11+
def ensure_operation_class!(qualified_name)
12+
klass = qualified_name.safe_constantize
13+
klass || use_defailt_operation(qualified_name)
14+
end
15+
16+
# This assumes the namespace already exists
17+
# This is only to be used if there does not exist
18+
# operation that goes by the name defined by
19+
# qualified_name (hence the warn log at the top)
20+
#
21+
# @param [String] qualified_name the name of the class to create
22+
# @example 'Api::V2::PostOperations::Create'
23+
#
24+
# @return [Class] a duplicate of the default operation
25+
def use_defailt_operation(qualified_name)
26+
SkinnyControllers.logger.warn("#{qualified_name} not found. Creating default...")
27+
28+
parts = qualified_name.split('::')
29+
class_name = parts.pop
30+
namespace = parts.join('::').safe_constantize
31+
32+
namespace.const_set(
33+
class_name,
34+
SkinnyControllers::Operation::Default.dup
35+
)
36+
end
37+
end
38+
39+
def ensure_policy_class!(qualified_name)
40+
qualified_name.safe_constantize ||
41+
Lookup::Policy.define_policy_class(qualified_name)
42+
end
43+
end
44+
end

lib/skinny_controllers/lookup/model.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# frozen_string_literal: true
22
module SkinnyControllers
3-
module Lookup
3+
class Lookup
44
module Model
55
module_function
66

lib/skinny_controllers/lookup/namespace.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# frozen_string_literal: true
22
module SkinnyControllers
3-
module Lookup
3+
class Lookup
44
module Namespace
55
module_function
66

lib/skinny_controllers/lookup/operation.rb

+1-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# frozen_string_literal: true
22
module SkinnyControllers
3-
module Lookup
3+
class Lookup
44
module Operation
55
module_function
66

@@ -15,15 +15,6 @@ def operation_of(model_name, verb)
1515
klass || default_operation_class_for(model_name, verb)
1616
end
1717

18-
# @param [String] controller name of the controller class
19-
# @param [String] verb
20-
# @param [String] model_name optional
21-
# @return [Class] the class or default
22-
def from_controller(controller, verb, model_name = nil)
23-
model_name ||= Lookup::Controller.model_name(controller)
24-
operation_of(model_name, verb)
25-
end
26-
2718
# dynamically creates a module for the model if it
2819
# isn't already defined
2920
# @return [Class] default operation class

lib/skinny_controllers/lookup/policy.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# frozen_string_literal: true
22
module SkinnyControllers
3-
module Lookup
3+
class Lookup
44
module Policy
55
module_function
66

0 commit comments

Comments
 (0)