objectify 0.0.3 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|