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