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
@@ -0,0 +1,14 @@
|
|
1
|
+
module Objectify
|
2
|
+
module Rails
|
3
|
+
module Helpers
|
4
|
+
def objectify_data
|
5
|
+
@_objectify_data ||
|
6
|
+
raise(ArgumentError, "No data was passed to this view.")
|
7
|
+
end
|
8
|
+
|
9
|
+
def policy_allowed?(policy_name)
|
10
|
+
objectify_executor.call(policy_name, :policy)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "active_support/all"
|
2
|
+
|
3
|
+
module Objectify
|
4
|
+
module Rails
|
5
|
+
class LogSubscriber < ActiveSupport::LogSubscriber
|
6
|
+
def start_processing(event)
|
7
|
+
logger.debug(" [Objectify] Started #{event.payload[:route]}")
|
8
|
+
end
|
9
|
+
|
10
|
+
def inject(event)
|
11
|
+
if logger.debug?
|
12
|
+
object = event.payload[:object]
|
13
|
+
method = event.payload[:method]
|
14
|
+
parameters = event.payload[:parameters].map do |req, param|
|
15
|
+
param if req == :req
|
16
|
+
end.compact
|
17
|
+
resolvers = Hash[*parameters.zip(event.payload[:resolvers].map(&:class)).flatten]
|
18
|
+
arguments = event.payload[:arguments].map(&:class).inspect[1..-2]
|
19
|
+
|
20
|
+
message = " [Injector] Invoking #{object}.#{method}(#{arguments}). "
|
21
|
+
message << "Resolutions: #{resolvers}. " if !resolvers.empty?
|
22
|
+
message << duration(event)
|
23
|
+
|
24
|
+
logger.debug(message)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def executor_start(event)
|
29
|
+
if logger.debug?
|
30
|
+
type = event.payload[:type].to_s.capitalize
|
31
|
+
logger.debug(" [#{type}] Executing #{event.payload[:name]} " + duration(event))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def executor(event)
|
36
|
+
if logger.debug?
|
37
|
+
type = event.payload[:type].to_s.capitalize
|
38
|
+
logger.debug(" [#{type}] Executed #{event.payload[:name]} " + duration(event))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def policy_chain_halted(event)
|
43
|
+
if logger.debug?
|
44
|
+
logger.debug(" [Policy] Chain halted at #{event.payload[:policy]}. Responding with #{event.payload[:responder]}." + duration(event))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def logger
|
49
|
+
::Rails.logger
|
50
|
+
end
|
51
|
+
|
52
|
+
def duration(event)
|
53
|
+
"#{"(%.1fms)" % event.duration}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
Objectify::Rails::LogSubscriber.attach_to(:objectify)
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Objectify
|
2
|
+
module Rails
|
3
|
+
class Renderer
|
4
|
+
def initialize(controller)
|
5
|
+
@controller = controller
|
6
|
+
end
|
7
|
+
|
8
|
+
def template(template, opts)
|
9
|
+
@controller.render opts.merge(:template => template)
|
10
|
+
end
|
11
|
+
|
12
|
+
def action(action, opts)
|
13
|
+
@controller.render opts.merge(:action => action)
|
14
|
+
end
|
15
|
+
|
16
|
+
def partial(partial, opts)
|
17
|
+
@controller.render opts.merge(:partial => partial)
|
18
|
+
end
|
19
|
+
|
20
|
+
def file(file, opts)
|
21
|
+
@controller.render opts.merge(:file => file)
|
22
|
+
end
|
23
|
+
|
24
|
+
def nothing(opts)
|
25
|
+
@controller.render opts.merge(:nothing => true)
|
26
|
+
end
|
27
|
+
|
28
|
+
def render(opts)
|
29
|
+
@controller.render opts
|
30
|
+
end
|
31
|
+
|
32
|
+
def respond_to
|
33
|
+
@controller.respond_to do |format|
|
34
|
+
yield(format)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def redirect_to(*args)
|
39
|
+
@controller.redirect_to(*args)
|
40
|
+
end
|
41
|
+
|
42
|
+
def data(data)
|
43
|
+
@controller.instance_variable_set(:@objectify_data, data)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require "action_dispatch"
|
2
|
+
require "objectify/config/action"
|
3
|
+
|
4
|
+
module Objectify
|
5
|
+
module Rails
|
6
|
+
module Routing
|
7
|
+
RESOURCE_ACTIONS = [:index, :show, :new, :create,
|
8
|
+
:edit, :update, :destroy].freeze
|
9
|
+
OBJECTIFY_OPTIONS = [:policies].freeze
|
10
|
+
|
11
|
+
class ObjectifyMapper
|
12
|
+
def initialize(rails_mapper,
|
13
|
+
application = ::Rails.application,
|
14
|
+
action_factory = Config::Action)
|
15
|
+
@rails_mapper = rails_mapper
|
16
|
+
@application = application
|
17
|
+
@action_factory = action_factory
|
18
|
+
end
|
19
|
+
|
20
|
+
def resources(*args)
|
21
|
+
options = args.extract_options!
|
22
|
+
objectify_options = extract_objectify_options(options)
|
23
|
+
controller = @application.objectify.objectify_controller
|
24
|
+
rails_options = options.merge(:controller => controller)
|
25
|
+
|
26
|
+
args.each do |resource_name|
|
27
|
+
objectify_defaults = {:objectify => {:resource => resource_name}}
|
28
|
+
defaults = (rails_options[:defaults] || {}).merge(objectify_defaults)
|
29
|
+
with_defaults = rails_options.merge(:defaults => defaults)
|
30
|
+
@rails_mapper.resources(resource_name, with_defaults)
|
31
|
+
|
32
|
+
RESOURCE_ACTIONS.each do |action_name|
|
33
|
+
action = @action_factory.new(resource_name,
|
34
|
+
action_name,
|
35
|
+
objectify_options,
|
36
|
+
@application.objectify.policies)
|
37
|
+
|
38
|
+
@application.objectify.append_action(action)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def defaults(options)
|
44
|
+
@application.objectify.append_defaults(options)
|
45
|
+
end
|
46
|
+
|
47
|
+
def policy_responders(options)
|
48
|
+
@application.objectify.append_policy_responders(options)
|
49
|
+
end
|
50
|
+
|
51
|
+
def resolutions(options)
|
52
|
+
@application.objectify.append_resolutions(options)
|
53
|
+
end
|
54
|
+
|
55
|
+
def legacy_action(controller, actions, options)
|
56
|
+
[*actions].each do |action_name|
|
57
|
+
action = @action_factory.new(controller,
|
58
|
+
action_name,
|
59
|
+
options,
|
60
|
+
@application.objectify.policies)
|
61
|
+
@application.objectify.append_action(action)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
def extract_objectify_options(options)
|
67
|
+
Hash[*(RESOURCE_ACTIONS + OBJECTIFY_OPTIONS).map do |key|
|
68
|
+
[key, options.delete(key)] if options.include?(key)
|
69
|
+
end.compact.flatten]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class Mapper < ActionDispatch::Routing::Mapper
|
74
|
+
def objectify
|
75
|
+
@objectify ||= ObjectifyMapper.new(self)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class RouteSet < ActionDispatch::Routing::RouteSet
|
80
|
+
def draw(&block)
|
81
|
+
clear! unless @disable_clear_and_finalize
|
82
|
+
|
83
|
+
mapper = Mapper.new(self)
|
84
|
+
if block.arity == 1
|
85
|
+
mapper.instance_exec(ActionDispatch::Routing::DeprecatedMapper.new(self), &block)
|
86
|
+
else
|
87
|
+
mapper.instance_exec(&block)
|
88
|
+
end
|
89
|
+
|
90
|
+
finalize! unless @disable_clear_and_finalize
|
91
|
+
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Objectify
|
2
|
+
class NamedValueResolver
|
3
|
+
attr_reader :name
|
4
|
+
|
5
|
+
def initialize(name, value)
|
6
|
+
@name = name
|
7
|
+
@value = value
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
@value
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class NameTranslationResolver
|
16
|
+
attr_reader :name
|
17
|
+
|
18
|
+
def initialize(name, value)
|
19
|
+
@name = name
|
20
|
+
@value = value.to_s.classify
|
21
|
+
end
|
22
|
+
|
23
|
+
def call
|
24
|
+
@value.constantize.new
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require "objectify/resolver"
|
2
|
+
|
3
|
+
module Objectify
|
4
|
+
class ArrayResolverLocator
|
5
|
+
def initialize(resolvers)
|
6
|
+
@resolvers = resolvers
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(name)
|
10
|
+
@resolvers.detect { |resolver| resolver.name == name }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class MultiResolverLocator
|
15
|
+
def initialize(locators)
|
16
|
+
@locators = [*locators]
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(name)
|
20
|
+
@locators.each do |locator|
|
21
|
+
resolver = locator.call(name)
|
22
|
+
return resolver if resolver
|
23
|
+
end
|
24
|
+
|
25
|
+
raise ArgumentError, "No resolver found for #{name}."
|
26
|
+
end
|
27
|
+
|
28
|
+
def context(context)
|
29
|
+
@locators.unshift(context)
|
30
|
+
end
|
31
|
+
|
32
|
+
def clear_context
|
33
|
+
@locators.shift
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class NamedValueResolverLocator
|
38
|
+
def initialize(resolver_factory = NamedValueResolver)
|
39
|
+
@resolver_factory = resolver_factory
|
40
|
+
@resolvers = {}
|
41
|
+
end
|
42
|
+
|
43
|
+
def add(name, value)
|
44
|
+
@resolvers[name] = @resolver_factory.new(name, value)
|
45
|
+
end
|
46
|
+
|
47
|
+
def call(name)
|
48
|
+
@resolvers[name]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class ConstResolverLocator
|
53
|
+
def initialize(namespace = nil)
|
54
|
+
@namespace = namespace
|
55
|
+
@cache = {}
|
56
|
+
end
|
57
|
+
|
58
|
+
def call(name)
|
59
|
+
@cache[name] || locate_and_store(name)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
def locate_and_store(name)
|
64
|
+
begin
|
65
|
+
const_name = [@namespace, [name, :resolver].join("_")].compact.join("/")
|
66
|
+
const_name.classify.constantize.new.tap do |obj|
|
67
|
+
@cache[name] = obj
|
68
|
+
end
|
69
|
+
rescue NameError
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/objectify/version.rb
CHANGED
data/objectify.gemspec
CHANGED
@@ -5,12 +5,11 @@ require "objectify/version"
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = "objectify"
|
7
7
|
s.version = Objectify::VERSION
|
8
|
-
s.
|
9
|
-
s.
|
10
|
-
s.email = ["akiellor@gmail.com"]
|
8
|
+
s.authors = ["James Golick"]
|
9
|
+
s.email = ["jamesgolick@gmail.com"]
|
11
10
|
s.homepage = ""
|
12
|
-
s.summary = %q{
|
13
|
-
s.description = %q{
|
11
|
+
s.summary = %q{}
|
12
|
+
s.description = %q{}
|
14
13
|
|
15
14
|
s.rubyforge_project = "objectify"
|
16
15
|
|
@@ -19,7 +18,13 @@ Gem::Specification.new do |s|
|
|
19
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
19
|
s.require_paths = ["lib"]
|
21
20
|
|
22
|
-
|
23
|
-
s.add_development_dependency
|
24
|
-
s.add_development_dependency
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
s.add_development_dependency "rspec", "~> 2.4.0"
|
23
|
+
s.add_development_dependency "bundler", "~> 1.0.0"
|
24
|
+
s.add_development_dependency "jeweler", "~> 1.6.4"
|
25
|
+
s.add_development_dependency "bourne", "1.0"
|
26
|
+
s.add_development_dependency "mocha", "0.9.8"
|
27
|
+
|
28
|
+
s.add_runtime_dependency "rails", ">=3.0.0"
|
29
|
+
s.add_runtime_dependency "i18n"
|
25
30
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "objectify/config/action"
|
3
|
+
|
4
|
+
describe "Objectify::Config::Action" do
|
5
|
+
before do
|
6
|
+
@merged_policies = stub("MergedPolicies")
|
7
|
+
@default_policies = stub("PolicyConf", :merge => @merged_policies)
|
8
|
+
|
9
|
+
@route = stub("Route")
|
10
|
+
@route_factory = stub("RouteFactory", :new => @route)
|
11
|
+
end
|
12
|
+
|
13
|
+
context "when everything is specified" do
|
14
|
+
before do
|
15
|
+
@index_options = {
|
16
|
+
:service => :something,
|
17
|
+
:responder => :pictures_index
|
18
|
+
}
|
19
|
+
|
20
|
+
@options = {
|
21
|
+
:policies => :asdf,
|
22
|
+
:skip_policies => :bsdf,
|
23
|
+
:index => @index_options
|
24
|
+
}
|
25
|
+
|
26
|
+
@action = Objectify::Config::Action.new(:pictures,
|
27
|
+
:index,
|
28
|
+
@options,
|
29
|
+
@default_policies,
|
30
|
+
@route_factory)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "creates and stores a route from the route factory" do
|
34
|
+
@route_factory.should have_received(:new).with(:pictures, :index)
|
35
|
+
@action.route.should == @route
|
36
|
+
end
|
37
|
+
|
38
|
+
it "stores its resource_name" do
|
39
|
+
@action.resource_name.should == :pictures
|
40
|
+
end
|
41
|
+
|
42
|
+
it "stores its name" do
|
43
|
+
@action.name.should == :index
|
44
|
+
end
|
45
|
+
|
46
|
+
it "merges and stores the policy configurations" do
|
47
|
+
@default_policies.should have_received(:merge).with(@options,
|
48
|
+
@index_options)
|
49
|
+
@action.policies.should == @merged_policies
|
50
|
+
end
|
51
|
+
|
52
|
+
it "stores service overrides" do
|
53
|
+
@action.service.should == :something
|
54
|
+
end
|
55
|
+
|
56
|
+
it "stores responder overrides" do
|
57
|
+
@action.responder.should == :pictures_index
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "when nothing is specified" do
|
62
|
+
before do
|
63
|
+
@options = {}
|
64
|
+
@action = Objectify::Config::Action.new(:pictures,
|
65
|
+
:index,
|
66
|
+
@options,
|
67
|
+
@default_policies,
|
68
|
+
@route_factory)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "has a default service of \#{resource_name}_\#{action_name}" do
|
72
|
+
@action.service.should == :pictures_index
|
73
|
+
end
|
74
|
+
|
75
|
+
it "has a default responder of \#{resource_name}_\#{action_name}" do
|
76
|
+
@action.responder.should == :pictures_index
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|