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