objectify 0.0.3 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.document +5 -0
  2. data/.gitignore +47 -2
  3. data/.rspec +1 -0
  4. data/Gemfile +1 -1
  5. data/Gemfile.lock +105 -16
  6. data/LICENSE.txt +20 -0
  7. data/README.md +230 -0
  8. data/Rakefile +14 -7
  9. data/lib/objectify.rb +1 -26
  10. data/lib/objectify/config/action.rb +31 -0
  11. data/lib/objectify/config/context.rb +97 -0
  12. data/lib/objectify/config/policies.rb +18 -0
  13. data/lib/objectify/executor.rb +21 -0
  14. data/lib/objectify/injector.rb +39 -0
  15. data/lib/objectify/instantiator.rb +14 -0
  16. data/lib/objectify/instrumentation.rb +12 -0
  17. data/lib/objectify/logging.rb +22 -0
  18. data/lib/objectify/policy_chain_executor.rb +27 -0
  19. data/lib/objectify/rails/application.rb +16 -0
  20. data/lib/objectify/rails/controller.rb +117 -0
  21. data/lib/objectify/rails/helpers.rb +14 -0
  22. data/lib/objectify/rails/log_subscriber.rb +59 -0
  23. data/lib/objectify/rails/railtie.rb +11 -0
  24. data/lib/objectify/rails/renderer.rb +47 -0
  25. data/lib/objectify/rails/routing.rb +97 -0
  26. data/lib/objectify/resolver.rb +27 -0
  27. data/lib/objectify/resolver_locator.rb +73 -0
  28. data/lib/objectify/route.rb +4 -0
  29. data/lib/objectify/version.rb +1 -1
  30. data/objectify.gemspec +13 -8
  31. data/spec/config/action_spec.rb +79 -0
  32. data/spec/config/context_spec.rb +137 -0
  33. data/spec/config/policies_spec.rb +23 -0
  34. data/spec/executor_spec.rb +47 -0
  35. data/spec/injector_spec.rb +88 -0
  36. data/spec/instantiator_spec.rb +22 -0
  37. data/spec/policy_chain_executor_spec.rb +60 -0
  38. data/spec/rails/renderer_spec.rb +63 -0
  39. data/spec/rails/routing_spec.rb +171 -0
  40. data/spec/resolver_locator_spec.rb +109 -0
  41. data/spec/resolver_spec.rb +28 -0
  42. data/spec/route_spec.rb +11 -0
  43. data/spec/spec_helper.rb +13 -5
  44. metadata +142 -62
  45. 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
- Bundler::GemHelper.install_tasks
8
-
9
- RSpec::Core::RakeTask.new :spec do |t|
10
- t.rspec_opts= '--color'
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 'active_support'
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,12 @@
1
+ require "active_support/all"
2
+
3
+ module Objectify
4
+ module Instrumentation
5
+ private
6
+ def instrument(name, payload={})
7
+ ActiveSupport::Notifications.instrument(name, payload) do |payload|
8
+ yield(payload) if block_given?
9
+ end
10
+ end
11
+ end
12
+ 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