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.
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