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
@@ -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,11 @@
1
+ module Objectify
2
+ module Rails
3
+ class Railtie < Rails::Railtie
4
+ initializer "objectify.reloader" do
5
+ ActionDispatch::Callbacks.to_prepare do
6
+ Rails.application.objectify.reload
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -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
@@ -0,0 +1,4 @@
1
+ module Objectify
2
+ class Route < Struct.new(:resource, :action)
3
+ end
4
+ end
@@ -1,3 +1,3 @@
1
1
  module Objectify
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.5"
3
3
  end
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.platform = Gem::Platform::RUBY
9
- s.authors = ["Andrew Kiellor"]
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{A simple gem to make hashes and arrays of hashes behave like objects.}
13
- s.description = %q{A simple gem to make hashes and arrays of hashes behave like objects using proxies.}
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
- s.add_dependency('activesupport', '>= 2.3')
23
- s.add_development_dependency('rspec')
24
- s.add_development_dependency('rake')
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