objectify 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.gitignore +47 -2
- data/.rspec +1 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +105 -16
- data/LICENSE.txt +20 -0
- data/README.md +230 -0
- data/Rakefile +14 -7
- data/lib/objectify.rb +1 -26
- data/lib/objectify/config/action.rb +31 -0
- data/lib/objectify/config/context.rb +97 -0
- data/lib/objectify/config/policies.rb +18 -0
- data/lib/objectify/executor.rb +21 -0
- data/lib/objectify/injector.rb +39 -0
- data/lib/objectify/instantiator.rb +14 -0
- data/lib/objectify/instrumentation.rb +12 -0
- data/lib/objectify/logging.rb +22 -0
- data/lib/objectify/policy_chain_executor.rb +27 -0
- data/lib/objectify/rails/application.rb +16 -0
- data/lib/objectify/rails/controller.rb +117 -0
- data/lib/objectify/rails/helpers.rb +14 -0
- data/lib/objectify/rails/log_subscriber.rb +59 -0
- data/lib/objectify/rails/railtie.rb +11 -0
- data/lib/objectify/rails/renderer.rb +47 -0
- data/lib/objectify/rails/routing.rb +97 -0
- data/lib/objectify/resolver.rb +27 -0
- data/lib/objectify/resolver_locator.rb +73 -0
- data/lib/objectify/route.rb +4 -0
- data/lib/objectify/version.rb +1 -1
- data/objectify.gemspec +13 -8
- data/spec/config/action_spec.rb +79 -0
- data/spec/config/context_spec.rb +137 -0
- data/spec/config/policies_spec.rb +23 -0
- data/spec/executor_spec.rb +47 -0
- data/spec/injector_spec.rb +88 -0
- data/spec/instantiator_spec.rb +22 -0
- data/spec/policy_chain_executor_spec.rb +60 -0
- data/spec/rails/renderer_spec.rb +63 -0
- data/spec/rails/routing_spec.rb +171 -0
- data/spec/resolver_locator_spec.rb +109 -0
- data/spec/resolver_spec.rb +28 -0
- data/spec/route_spec.rb +11 -0
- data/spec/spec_helper.rb +13 -5
- metadata +142 -62
- data/spec/objectify_spec.rb +0 -60
data/Rakefile
CHANGED
@@ -1,13 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'rubygems'
|
2
|
-
require 'rake'
|
3
|
-
require 'rake/gempackagetask'
|
4
|
-
require 'rspec/core/rake_task'
|
5
4
|
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
6
13
|
|
7
|
-
|
8
|
-
|
9
|
-
RSpec::Core::RakeTask.new
|
10
|
-
|
14
|
+
require 'rspec/core'
|
15
|
+
require 'rspec/core/rake_task'
|
16
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
17
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
11
18
|
end
|
12
19
|
|
13
20
|
task :default => :spec
|
data/lib/objectify.rb
CHANGED
@@ -1,29 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "objectify/railtie"
|
2
2
|
|
3
3
|
module Objectify
|
4
|
-
class Proxy < ActiveSupport::BasicObject
|
5
|
-
def initialize target
|
6
|
-
@target = target
|
7
|
-
end
|
8
|
-
|
9
|
-
def method_missing name, *args, &block
|
10
|
-
if @target.respond_to?(:has_key?) && @target.has_key?(name)
|
11
|
-
Proxy.new @target[name]
|
12
|
-
else
|
13
|
-
Proxy.new @target.send(name, *args, &block)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def send name, *args, &block
|
18
|
-
method_missing name.to_sym, *args, &block
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def objectify
|
23
|
-
Proxy.new self
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def Objectify object
|
28
|
-
Objectify::Proxy.new object
|
29
4
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "objectify/config/policies"
|
2
|
+
require "objectify/route"
|
3
|
+
|
4
|
+
module Objectify
|
5
|
+
module Config
|
6
|
+
class Action
|
7
|
+
attr_reader :resource_name, :name, :route, :policies, :service, :responder
|
8
|
+
|
9
|
+
def initialize(resource_name, name, options, default_policies,
|
10
|
+
route_factory = Route)
|
11
|
+
@route = route_factory.new(resource_name, name)
|
12
|
+
@resource_name = resource_name
|
13
|
+
@name = name
|
14
|
+
@policies = default_policies.merge(options, options[name])
|
15
|
+
|
16
|
+
if options[name]
|
17
|
+
@service = options[name][:service]
|
18
|
+
@responder = options[name][:responder]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def service
|
23
|
+
@service ||= [resource_name, name].join("_").to_sym
|
24
|
+
end
|
25
|
+
|
26
|
+
def responder
|
27
|
+
@responder ||= [resource_name, name].join("_").to_sym
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require "objectify/config/action"
|
2
|
+
require "objectify/config/policies"
|
3
|
+
require "objectify/injector"
|
4
|
+
require "objectify/instantiator"
|
5
|
+
require "objectify/resolver"
|
6
|
+
require "objectify/resolver_locator"
|
7
|
+
require "objectify/executor"
|
8
|
+
|
9
|
+
module Objectify
|
10
|
+
module Config
|
11
|
+
class Context
|
12
|
+
DONT_RELOAD = [:@objectify_controller].freeze
|
13
|
+
|
14
|
+
attr_reader :policy_responders, :defaults, :actions, :policies
|
15
|
+
attr_writer :injector, :resolver_locator, :instantiator, :executor,
|
16
|
+
:locator, :objectify_controller
|
17
|
+
|
18
|
+
def initialize(policies_factory = Policies, action_factory = Action)
|
19
|
+
@policies_factory = policies_factory
|
20
|
+
@action_factory = action_factory
|
21
|
+
|
22
|
+
@policy_responders = {}
|
23
|
+
@defaults = {}
|
24
|
+
@actions = {}
|
25
|
+
end
|
26
|
+
|
27
|
+
def append_policy_responders(responders)
|
28
|
+
@policy_responders.merge!(responders)
|
29
|
+
end
|
30
|
+
|
31
|
+
def policy_responder(policy)
|
32
|
+
@policy_responders[policy] ||
|
33
|
+
raise(ArgumentError, "Can't find a responder for #{policy}.")
|
34
|
+
end
|
35
|
+
|
36
|
+
def policies
|
37
|
+
@policies ||= @policies_factory.new
|
38
|
+
end
|
39
|
+
|
40
|
+
def append_defaults(defaults)
|
41
|
+
@policies = @policies_factory.new(defaults)
|
42
|
+
end
|
43
|
+
|
44
|
+
def append_action(action)
|
45
|
+
@actions[action.route] = action
|
46
|
+
end
|
47
|
+
|
48
|
+
def action(route)
|
49
|
+
@actions[route] ||
|
50
|
+
raise(ArgumentError, "No action matching #{route} was found.")
|
51
|
+
end
|
52
|
+
|
53
|
+
def legacy_action(route)
|
54
|
+
@actions[route] ||
|
55
|
+
@action_factory.new(route.resource, route.action, {}, policies)
|
56
|
+
end
|
57
|
+
|
58
|
+
def injector
|
59
|
+
@injector ||= Injector.new(resolver_locator)
|
60
|
+
end
|
61
|
+
|
62
|
+
def append_resolutions(opts)
|
63
|
+
opts.each do |k,v|
|
64
|
+
locator.add(k, v)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def locator
|
69
|
+
@locator ||= NamedValueResolverLocator.new(NameTranslationResolver)
|
70
|
+
end
|
71
|
+
|
72
|
+
def resolver_locator
|
73
|
+
@resolver_locator ||= MultiResolverLocator.new(
|
74
|
+
[locator, ConstResolverLocator.new]
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
def instantiator
|
79
|
+
@instantiator ||= Instantiator.new(injector)
|
80
|
+
end
|
81
|
+
|
82
|
+
def executor
|
83
|
+
@executor ||= Executor.new(injector, instantiator)
|
84
|
+
end
|
85
|
+
|
86
|
+
def objectify_controller
|
87
|
+
@objectify_controller ||= "objectify/rails/objectify"
|
88
|
+
end
|
89
|
+
|
90
|
+
def reload
|
91
|
+
instance_variables.each do |name|
|
92
|
+
instance_variable_set(name, nil) unless DONT_RELOAD.include?(name)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Objectify
|
2
|
+
module Config
|
3
|
+
class Policies
|
4
|
+
attr_reader :policies, :skip_policies
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
@policies = [*options[:policies]]
|
8
|
+
@skip_policies = [*options[:skip_policies]]
|
9
|
+
end
|
10
|
+
|
11
|
+
def merge(*others)
|
12
|
+
others.compact.inject(@policies - @skip_policies) do |total, opts|
|
13
|
+
total + [*opts[:policies]] - [*opts[:skip_policies]]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "objectify/instrumentation"
|
2
|
+
|
3
|
+
module Objectify
|
4
|
+
class Executor
|
5
|
+
include Instrumentation
|
6
|
+
|
7
|
+
def initialize(injector, instantiator)
|
8
|
+
@injector = injector
|
9
|
+
@instantiator = instantiator
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(name, type)
|
13
|
+
method = type == :policy ? :allowed? : :call
|
14
|
+
opts = { :name => name, :type => type }
|
15
|
+
instrument("executor_start.objectify", opts)
|
16
|
+
instrument("executor.objectify", opts) do
|
17
|
+
@injector.call(@instantiator.call(name, type), method)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "objectify/instrumentation"
|
2
|
+
|
3
|
+
module Objectify
|
4
|
+
class Injector
|
5
|
+
include Instrumentation
|
6
|
+
|
7
|
+
def initialize(resolver_locator)
|
8
|
+
@resolver_locator = resolver_locator
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(object, method)
|
12
|
+
payload = {:object => object, :method => method}
|
13
|
+
instrument("inject.objectify", payload) do |payload|
|
14
|
+
method_obj = method_object(object, method)
|
15
|
+
resolvers = method_obj.parameters.map do |reqd, name|
|
16
|
+
@resolver_locator.call(name) if reqd == :req
|
17
|
+
end.compact
|
18
|
+
arguments = resolvers.map do |resolver|
|
19
|
+
call(resolver, :call)
|
20
|
+
end
|
21
|
+
|
22
|
+
payload[:parameters] = method_obj.parameters
|
23
|
+
payload[:resolvers] = resolvers
|
24
|
+
payload[:arguments] = arguments
|
25
|
+
|
26
|
+
object.send(method, *arguments)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def method_object(object, method)
|
32
|
+
if method == :new
|
33
|
+
object.instance_method(:initialize)
|
34
|
+
else
|
35
|
+
object.method(method)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "active_support/all"
|
2
|
+
|
3
|
+
module Objectify
|
4
|
+
class Instantiator
|
5
|
+
def initialize(injector)
|
6
|
+
@injector = injector
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(name, type)
|
10
|
+
klass = [name, type].join("_").classify.constantize
|
11
|
+
@injector.call(klass, :new)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "logger"
|
2
|
+
|
3
|
+
module Objectify
|
4
|
+
module Logging
|
5
|
+
class << self
|
6
|
+
attr_writer :logger
|
7
|
+
|
8
|
+
def logger
|
9
|
+
@logger ||= Logger.new("/dev/null")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
def logger
|
15
|
+
if Kernel.const_defined?(:Rails)
|
16
|
+
::Rails.logger
|
17
|
+
else
|
18
|
+
Logging.logger
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "objectify/instrumentation"
|
2
|
+
|
3
|
+
module Objectify
|
4
|
+
class PolicyChainExecutor
|
5
|
+
include Instrumentation
|
6
|
+
|
7
|
+
def initialize(executor, context)
|
8
|
+
@executor = executor
|
9
|
+
@context = context
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(action)
|
13
|
+
action.policies.each do |policy|
|
14
|
+
if !@executor.call(policy, :policy)
|
15
|
+
responder = @context.policy_responder(policy)
|
16
|
+
instrument("policy_chain_halted.objectify", :policy => policy,
|
17
|
+
:responder => responder)
|
18
|
+
@executor.call(responder, :responder)
|
19
|
+
|
20
|
+
return false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "objectify/rails/routing"
|
2
|
+
require "objectify/config/context"
|
3
|
+
|
4
|
+
module Objectify
|
5
|
+
module Rails
|
6
|
+
module Application
|
7
|
+
def objectify
|
8
|
+
@objectify ||= Config::Context.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def routes
|
12
|
+
@routes ||= Routing::RouteSet.new
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require "objectify/resolver_locator"
|
2
|
+
require "objectify/executor"
|
3
|
+
require "objectify/policy_chain_executor"
|
4
|
+
require "objectify/instrumentation"
|
5
|
+
require "objectify/rails/renderer"
|
6
|
+
|
7
|
+
module Objectify
|
8
|
+
module Rails
|
9
|
+
module ControllerHelpers
|
10
|
+
def self.included(klass)
|
11
|
+
klass.helper_method(:objectify_executor) if klass.respond_to?(:helper_method)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def objectify
|
16
|
+
::Rails.application.objectify
|
17
|
+
end
|
18
|
+
|
19
|
+
def injector
|
20
|
+
objectify.injector
|
21
|
+
end
|
22
|
+
|
23
|
+
def request_resolver
|
24
|
+
klass = Objectify::NamedValueResolverLocator
|
25
|
+
@request_resolver ||= klass.new.tap do |resolver|
|
26
|
+
resolver.add(:controller, self)
|
27
|
+
resolver.add(:params, params)
|
28
|
+
resolver.add(:session, session)
|
29
|
+
resolver.add(:cookies, cookies)
|
30
|
+
resolver.add(:request, request)
|
31
|
+
resolver.add(:response, response)
|
32
|
+
resolver.add(:flash, flash)
|
33
|
+
resolver.add(:renderer, Renderer.new(self))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def objectify_executor
|
38
|
+
objectify.executor
|
39
|
+
end
|
40
|
+
|
41
|
+
def policy_chain_executor
|
42
|
+
@policy_chain_executor ||= Objectify::PolicyChainExecutor.new(objectify_executor, objectify)
|
43
|
+
end
|
44
|
+
|
45
|
+
def objectify_route
|
46
|
+
@objectify_route ||= if params[:objectify]
|
47
|
+
Objectify::Route.new(params[:objectify][:resource].to_sym,
|
48
|
+
params[:action].to_sym)
|
49
|
+
else
|
50
|
+
Objectify::Route.new(params[:controller].to_sym,
|
51
|
+
params[:action].to_sym)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def action
|
56
|
+
@action ||= if params[:objectify]
|
57
|
+
objectify.action(objectify_route)
|
58
|
+
else
|
59
|
+
objectify.legacy_action(objectify_route)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def execute_policy_chain
|
64
|
+
policy_chain_executor.call(action)
|
65
|
+
end
|
66
|
+
|
67
|
+
def objectify_around_filter
|
68
|
+
objectify.resolver_locator.context(request_resolver)
|
69
|
+
yield
|
70
|
+
objectify.resolver_locator.clear_context
|
71
|
+
end
|
72
|
+
|
73
|
+
def execute_objectify_action
|
74
|
+
service_result = objectify_executor.call(action.service, :service)
|
75
|
+
request_resolver.add(:service_result, service_result)
|
76
|
+
|
77
|
+
objectify_executor.call(action.responder, :responder)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
module LegacyControllerBehaviour
|
82
|
+
include ControllerHelpers
|
83
|
+
include Instrumentation
|
84
|
+
|
85
|
+
def self.included(klass)
|
86
|
+
klass.helper_method(:objectify_executor) if klass.respond_to?(:helper_method)
|
87
|
+
end
|
88
|
+
|
89
|
+
def method_missing(name, *args, &block)
|
90
|
+
instrument("start_processing.objectify", :route => objectify_route)
|
91
|
+
|
92
|
+
execute_objectify_action
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
module ControllerBehaviour
|
97
|
+
include ControllerHelpers
|
98
|
+
include Instrumentation
|
99
|
+
|
100
|
+
def self.included(klass)
|
101
|
+
klass.helper_method(:objectify_executor) if klass.respond_to?(:helper_method)
|
102
|
+
end
|
103
|
+
|
104
|
+
def method_missing(name, *args, &block)
|
105
|
+
instrument("start_processing.objectify", :route => objectify_route)
|
106
|
+
|
107
|
+
if execute_policy_chain
|
108
|
+
execute_objectify_action
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class ObjectifyController < ActionController::Base
|
114
|
+
include ControllerBehaviour
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|