From 224e27e74df1657ed0e58dccc3b6252afab9edbc Mon Sep 17 00:00:00 2001 From: Marten Veldthuis Date: Fri, 19 Jul 2013 10:51:31 +0200 Subject: [PATCH 1/9] Add backwards-compatibility --- lib/pavlov/alpha_compatibility.rb | 44 +++++++++++++++++ lib/pavlov/operation.rb | 4 -- spec/integration/alpha_compatibility_spec.rb | 52 ++++++++++++++++++++ 3 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 lib/pavlov/alpha_compatibility.rb create mode 100644 spec/integration/alpha_compatibility_spec.rb diff --git a/lib/pavlov/alpha_compatibility.rb b/lib/pavlov/alpha_compatibility.rb new file mode 100644 index 0000000..5172275 --- /dev/null +++ b/lib/pavlov/alpha_compatibility.rb @@ -0,0 +1,44 @@ +require 'pavlov' + +module Pavlov + def self.command command_name, *args + class_name = "Commands::"+string_to_classname(command_name) + klass = get_class_by_string(class_name) + attributes = arguments_to_attributes(klass, args) + klass.new(attributes).call + end + + def self.interactor command_name, *args + class_name = "Interactors::"+string_to_classname(command_name) + klass = get_class_by_string class_name + attributes = arguments_to_attributes(klass, args) + klass.new(attributes).call + end + + def self.query command_name, *args + class_name = "Queries::"+string_to_classname(command_name) + klass = get_class_by_string class_name + attributes = arguments_to_attributes(klass, args) + klass.new(attributes).call + end + + def self.arguments_to_attributes(operation_class, arguments) + attribute_keys = operation_class.attribute_set.map(&:name) + attributes_and_arguments = attribute_keys.zip(arguments) + Hash[attributes_and_arguments] + end + + module Operation + module ClassMethods + def arguments(*args) + # Add generic attribute for each argument + args.each do |argument| + attribute argument, Object + end + + # Add attribute for pavlov_options + attribute :pavlov_options, Hash + end + end + end +end \ No newline at end of file diff --git a/lib/pavlov/operation.rb b/lib/pavlov/operation.rb index c24d9b1..b054a78 100644 --- a/lib/pavlov/operation.rb +++ b/lib/pavlov/operation.rb @@ -27,10 +27,6 @@ def call(*args, &block) private - def pavlov_options - @options - end - def raise_unauthorized(message='Unauthorized') raise Pavlov::AccessDenied, message end diff --git a/spec/integration/alpha_compatibility_spec.rb b/spec/integration/alpha_compatibility_spec.rb new file mode 100644 index 0000000..b8eaad7 --- /dev/null +++ b/spec/integration/alpha_compatibility_spec.rb @@ -0,0 +1,52 @@ +require_relative '../spec_helper' +require 'pavlov' +require 'pavlov/alpha_compatibility' + +describe "Pavlov Alpha Compatibility" do + before do + stub_const "Queries", Module.new + stub_const "Commands", Module.new + stub_const "Interactors", Module.new + + module Interactors + class OldStyleInteractor + include Pavlov::Interactor + + arguments :title, :published + + private + + def authorized? + pavlov_options[:current_user].name == "John" + end + + def execute + if published + title.upcase! + else + title + end + end + end + end + end + + include Pavlov::Helpers + + let(:user) { double(name: "John") } + + def pavlov_options + {current_user: user} + end + + it 'supports old-style arguments definition' do + expect(interactor(:old_style_interactor, 'foo', true)).to eq('FOO') + end + + it 'passes the pavlov_options' do + user.stub(name: 'David') + expect { + interactor(:old_style_interactor, 'foo', true) + }.to raise_error(Pavlov::AccessDenied) + end +end From 2f885e465641cedadefafd3a21271efedfc060b2 Mon Sep 17 00:00:00 2001 From: Marten Veldthuis Date: Fri, 19 Jul 2013 14:01:28 +0200 Subject: [PATCH 2/9] Set default value for pavlov_options --- lib/pavlov/alpha_compatibility.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pavlov/alpha_compatibility.rb b/lib/pavlov/alpha_compatibility.rb index 5172275..96a40c1 100644 --- a/lib/pavlov/alpha_compatibility.rb +++ b/lib/pavlov/alpha_compatibility.rb @@ -37,7 +37,7 @@ def arguments(*args) end # Add attribute for pavlov_options - attribute :pavlov_options, Hash + attribute :pavlov_options, Hash, default: {} end end end From ffc52adc436bcc04e52ff5e851bc04b723f9aac9 Mon Sep 17 00:00:00 2001 From: Marten Veldthuis Date: Fri, 19 Jul 2013 14:02:19 +0200 Subject: [PATCH 3/9] Add specs for passing of pavlov_options between operations --- spec/integration/alpha_compatibility_spec.rb | 71 +++++++++++--------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/spec/integration/alpha_compatibility_spec.rb b/spec/integration/alpha_compatibility_spec.rb index b8eaad7..9abe874 100644 --- a/spec/integration/alpha_compatibility_spec.rb +++ b/spec/integration/alpha_compatibility_spec.rb @@ -3,50 +3,61 @@ require 'pavlov/alpha_compatibility' describe "Pavlov Alpha Compatibility" do - before do - stub_const "Queries", Module.new - stub_const "Commands", Module.new - stub_const "Interactors", Module.new + include Pavlov::Helpers - module Interactors - class OldStyleInteractor - include Pavlov::Interactor + describe 'retains .arguments' do + before do + stub_const "Interactors", Module.new + class Interactors::OldStyleInteractor + include Pavlov::Interactor arguments :title, :published - private - def authorized? - pavlov_options[:current_user].name == "John" + true end def execute - if published - title.upcase! - else - title - end + published ? title.upcase! : title end end end - end - include Pavlov::Helpers + it 'supports old-style arguments definition' do + expect(interactor(:old_style_interactor, 'foo', false)).to eq('foo') + expect(interactor(:old_style_interactor, 'foo', true)).to eq('FOO') + end + end - let(:user) { double(name: "John") } + describe 'retains pavlov_options' do + let(:current_user) { double("User", name: "John") } + def pavlov_options + {current_user: current_user} + end - def pavlov_options - {current_user: user} - end + before do + stub_const "Queries", Module.new + stub_const "Interactors", Module.new + class Queries::FindUppercaseName + include Pavlov::Query + arguments + def execute + pavlov_options[:current_user].name.upcase + end + end - it 'supports old-style arguments definition' do - expect(interactor(:old_style_interactor, 'foo', true)).to eq('FOO') - end + class Interactors::ShoutyGreeting + include Pavlov::Interactor + arguments + def authorized?; true; end + def execute + "OHAI, #{query :find_uppercase_name}" + end + end + end - it 'passes the pavlov_options' do - user.stub(name: 'David') - expect { - interactor(:old_style_interactor, 'foo', true) - }.to raise_error(Pavlov::AccessDenied) + it 'passes the pavlov_options from operation to operation' do + expect(interactor :shouty_greeting).to eq("OHAI, JOHN") + end end -end +end \ No newline at end of file From 53346db49f42746931b9e63cfb1a6b714aa598da Mon Sep 17 00:00:00 2001 From: tomdev Date: Tue, 23 Jul 2013 13:46:56 +0200 Subject: [PATCH 4/9] Update changelog --- CHANGELOG.md | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bdf35e5..8174d3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,24 @@ -# HEAD +# Pavlov Changelog -Lots and lots of backwards incompatible breakage. +## HEAD + +This release brings forth lots and lots of incompatibilities. Where possible, we've tried to keep a backwards-compatible API available. You can activate this by requiring `pavlov/alpha_compatibility'. + +#### New Stuff: + +* Pavlov now uses Virtus for what used to be called `arguments`. Instead of specifying a list of arguments, you can now specify attributes individually, with optional defaults. Check the README on all the cool stuff you can do with these Virtus-based attributes. + +#### Deprecations: + +If you want to retain deprecated functionality, you can `require 'pavlov/alpha_compatibility'`. + +* Deprecated `arguments` in operations. +* Deprecated `pavlov_options` that were used by the helpers. + +#### Completely removed: -* Now using Virtus for what used to be called `arguments`. Check the README on how to use the new `attribute`s. * Removed support for `finish_initialize`. Override the `initialize` method and call `super` instead. -# 0.1.0 +## 0.1.0 Initial alpha-release. Here be dragons. From 02d2101eef7d3438149cfdfced6308e20f3fdac2 Mon Sep 17 00:00:00 2001 From: Marten Veldthuis Date: Fri, 19 Jul 2013 14:16:03 +0200 Subject: [PATCH 5/9] Deprecate Pavlov.command/query/interactor --- lib/pavlov.rb | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/lib/pavlov.rb b/lib/pavlov.rb index 5d3c05b..2257c91 100644 --- a/lib/pavlov.rb +++ b/lib/pavlov.rb @@ -8,24 +8,6 @@ def self.get_class_by_string classname def self.string_to_classname string string.to_s.camelize end - - def self.command command_name, *args - class_name = "Commands::"+string_to_classname(command_name) - klass = get_class_by_string(class_name) - klass.new(*args).call - end - - def self.interactor command_name, *args - class_name = "Interactors::"+string_to_classname(command_name) - klass = get_class_by_string class_name - klass.new(*args).call - end - - def self.query command_name, *args - class_name = "Queries::"+string_to_classname(command_name) - klass = get_class_by_string class_name - klass.new(*args).call - end end require_relative 'pavlov/engine' if defined?(Rails) From 1a7a786ecebebc466c928c9998f3f7d96dc1cbfc Mon Sep 17 00:00:00 2001 From: tomdev Date: Tue, 23 Jul 2013 14:29:37 +0200 Subject: [PATCH 6/9] Not deprecating old style command/interactor/query calls --- lib/pavlov.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/pavlov.rb b/lib/pavlov.rb index 2257c91..5d3c05b 100644 --- a/lib/pavlov.rb +++ b/lib/pavlov.rb @@ -8,6 +8,24 @@ def self.get_class_by_string classname def self.string_to_classname string string.to_s.camelize end + + def self.command command_name, *args + class_name = "Commands::"+string_to_classname(command_name) + klass = get_class_by_string(class_name) + klass.new(*args).call + end + + def self.interactor command_name, *args + class_name = "Interactors::"+string_to_classname(command_name) + klass = get_class_by_string class_name + klass.new(*args).call + end + + def self.query command_name, *args + class_name = "Queries::"+string_to_classname(command_name) + klass = get_class_by_string class_name + klass.new(*args).call + end end require_relative 'pavlov/engine' if defined?(Rails) From db768892f7e498285e0c010158358c914310ca4c Mon Sep 17 00:00:00 2001 From: tomdev Date: Tue, 23 Jul 2013 15:08:41 +0200 Subject: [PATCH 7/9] Changed compatibility layer for initialization This breaks for all old code, but this can be fixed easily by changes these method names. After that you can upgrade one by one by changing the method name back into `query`, `command` or `interactor`. --- lib/pavlov/alpha_compatibility.rb | 40 ++++++++++++++++++-- spec/integration/alpha_compatibility_spec.rb | 10 ++--- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/lib/pavlov/alpha_compatibility.rb b/lib/pavlov/alpha_compatibility.rb index 96a40c1..b6ba2b4 100644 --- a/lib/pavlov/alpha_compatibility.rb +++ b/lib/pavlov/alpha_compatibility.rb @@ -1,21 +1,21 @@ require 'pavlov' module Pavlov - def self.command command_name, *args + def self.old_command command_name, *args class_name = "Commands::"+string_to_classname(command_name) klass = get_class_by_string(class_name) attributes = arguments_to_attributes(klass, args) klass.new(attributes).call end - def self.interactor command_name, *args + def self.old_interactor command_name, *args class_name = "Interactors::"+string_to_classname(command_name) klass = get_class_by_string class_name attributes = arguments_to_attributes(klass, args) klass.new(attributes).call end - def self.query command_name, *args + def self.old_query command_name, *args class_name = "Queries::"+string_to_classname(command_name) klass = get_class_by_string class_name attributes = arguments_to_attributes(klass, args) @@ -28,6 +28,38 @@ def self.arguments_to_attributes(operation_class, arguments) Hash[attributes_and_arguments] end + module Helpers + def old_interactor name, *args + args = add_pavlov_options args + Pavlov.old_interactor name, *args + end + + def old_query name, *args + args = add_pavlov_options args + Pavlov.old_query name, *args + end + + def old_command name, *args + args = add_pavlov_options args + Pavlov.old_command name, *args + end + + def pavlov_options + {} + end + + private + def add_pavlov_options args + # TODO: we should do this at a point where we know how many arguments we need + # so we can decide if we need to merge with another options object or + # just add it. + if pavlov_options != {} + args << pavlov_options + end + args + end + end + module Operation module ClassMethods def arguments(*args) @@ -41,4 +73,4 @@ def arguments(*args) end end end -end \ No newline at end of file +end diff --git a/spec/integration/alpha_compatibility_spec.rb b/spec/integration/alpha_compatibility_spec.rb index 9abe874..103f51f 100644 --- a/spec/integration/alpha_compatibility_spec.rb +++ b/spec/integration/alpha_compatibility_spec.rb @@ -24,8 +24,8 @@ def execute end it 'supports old-style arguments definition' do - expect(interactor(:old_style_interactor, 'foo', false)).to eq('foo') - expect(interactor(:old_style_interactor, 'foo', true)).to eq('FOO') + expect(old_interactor(:old_style_interactor, 'foo', false)).to eq('foo') + expect(old_interactor(:old_style_interactor, 'foo', true)).to eq('FOO') end end @@ -51,13 +51,13 @@ class Interactors::ShoutyGreeting arguments def authorized?; true; end def execute - "OHAI, #{query :find_uppercase_name}" + "OHAI, #{old_query :find_uppercase_name}" end end end it 'passes the pavlov_options from operation to operation' do - expect(interactor :shouty_greeting).to eq("OHAI, JOHN") + expect(old_interactor :shouty_greeting).to eq("OHAI, JOHN") end end -end \ No newline at end of file +end From 02d05883003b189fcd12a1ce086e0f28eb9489b1 Mon Sep 17 00:00:00 2001 From: jjoos Date: Thu, 25 Jul 2013 11:09:04 +0200 Subject: [PATCH 8/9] Fixing name collisions, fixed adding pavlov options, removed creating pavlov_options in compatablity layer. --- lib/pavlov/alpha_compatibility.rb | 15 ++++----------- lib/pavlov/helpers.rb | 28 +++++++++++++--------------- 2 files changed, 17 insertions(+), 26 deletions(-) diff --git a/lib/pavlov/alpha_compatibility.rb b/lib/pavlov/alpha_compatibility.rb index b6ba2b4..acb55cb 100644 --- a/lib/pavlov/alpha_compatibility.rb +++ b/lib/pavlov/alpha_compatibility.rb @@ -30,26 +30,22 @@ def self.arguments_to_attributes(operation_class, arguments) module Helpers def old_interactor name, *args - args = add_pavlov_options args + args = add_compatablity_pavlov_options args Pavlov.old_interactor name, *args end def old_query name, *args - args = add_pavlov_options args + args = add_compatablity_pavlov_options args Pavlov.old_query name, *args end def old_command name, *args - args = add_pavlov_options args + args = add_compatablity_pavlov_options args Pavlov.old_command name, *args end - def pavlov_options - {} - end - private - def add_pavlov_options args + def add_compatablity_pavlov_options args # TODO: we should do this at a point where we know how many arguments we need # so we can decide if we need to merge with another options object or # just add it. @@ -67,9 +63,6 @@ def arguments(*args) args.each do |argument| attribute argument, Object end - - # Add attribute for pavlov_options - attribute :pavlov_options, Hash, default: {} end end end diff --git a/lib/pavlov/helpers.rb b/lib/pavlov/helpers.rb index f166707..d73c23e 100644 --- a/lib/pavlov/helpers.rb +++ b/lib/pavlov/helpers.rb @@ -1,18 +1,18 @@ module Pavlov module Helpers - def interactor name, *args - args = add_pavlov_options args - Pavlov.interactor name, *args + def interactor name, hash + hash = add_pavlov_options hash + Pavlov.interactor name, hash end - def query name, *args - args = add_pavlov_options args - Pavlov.query name, *args + def query name, hash + hash = add_pavlov_options hash + Pavlov.query name, hash end - def command name, *args - args = add_pavlov_options args - Pavlov.command name, *args + def command name, hash + hash = add_pavlov_options hash + Pavlov.command name, hash end def pavlov_options @@ -20,14 +20,12 @@ def pavlov_options end private - def add_pavlov_options args - # TODO: we should do this at a point where we know how many arguments we need - # so we can decide if we need to merge with another options object or - # just add it. + def add_pavlov_options hash if pavlov_options != {} - args << pavlov_options + hash ||= {} + hash[:pavlov_options] = pavlov_options.merge(hash[:pavlov_options]) end - args + hash end end end From e3b20a3dcbb9e3bad17af402b175860fa6259e8a Mon Sep 17 00:00:00 2001 From: jjoos Date: Thu, 25 Jul 2013 14:44:00 +0200 Subject: [PATCH 9/9] fix test, rename, always include comatablity Fixed tests (must have explicit pavlov_options) rename in tests interactor => old interactor always compatability for now --- lib/pavlov.rb | 1 + lib/pavlov/alpha_compatibility.rb | 9 +++++++-- lib/pavlov/helpers.rb | 7 ++++++- spec/integration/alpha_compatibility_spec.rb | 8 ++++++-- spec/pavlov/helpers_spec.rb | 10 +++++----- 5 files changed, 25 insertions(+), 10 deletions(-) diff --git a/lib/pavlov.rb b/lib/pavlov.rb index 5d3c05b..cd2807f 100644 --- a/lib/pavlov.rb +++ b/lib/pavlov.rb @@ -38,3 +38,4 @@ def self.query command_name, *args require_relative 'pavlov/query' require_relative 'pavlov/interactor' require_relative 'pavlov/version' +require_relative 'pavlov/alpha_compatibility' diff --git a/lib/pavlov/alpha_compatibility.rb b/lib/pavlov/alpha_compatibility.rb index acb55cb..484baeb 100644 --- a/lib/pavlov/alpha_compatibility.rb +++ b/lib/pavlov/alpha_compatibility.rb @@ -24,8 +24,13 @@ def self.old_query command_name, *args def self.arguments_to_attributes(operation_class, arguments) attribute_keys = operation_class.attribute_set.map(&:name) - attributes_and_arguments = attribute_keys.zip(arguments) - Hash[attributes_and_arguments] + + # TODO: this can be done so much better, but I don't know how. + hash={} + arguments.each_with_index do |value, index| + hash[attribute_keys[index].to_sym] = value + end + return hash end module Helpers diff --git a/lib/pavlov/helpers.rb b/lib/pavlov/helpers.rb index d73c23e..7107d04 100644 --- a/lib/pavlov/helpers.rb +++ b/lib/pavlov/helpers.rb @@ -23,7 +23,12 @@ def pavlov_options def add_pavlov_options hash if pavlov_options != {} hash ||= {} - hash[:pavlov_options] = pavlov_options.merge(hash[:pavlov_options]) + + if hash.has_key? 'pavlov_options' + hash[:pavlov_options] = pavlov_options.merge(hash[:pavlov_options]) + else + hash[:pavlov_options] = pavlov_options + end end hash end diff --git a/spec/integration/alpha_compatibility_spec.rb b/spec/integration/alpha_compatibility_spec.rb index 103f51f..8698eae 100644 --- a/spec/integration/alpha_compatibility_spec.rb +++ b/spec/integration/alpha_compatibility_spec.rb @@ -40,7 +40,9 @@ def pavlov_options stub_const "Interactors", Module.new class Queries::FindUppercaseName include Pavlov::Query - arguments + + attribute :pavlov_options, Hash, default: {} + def execute pavlov_options[:current_user].name.upcase end @@ -48,7 +50,9 @@ def execute class Interactors::ShoutyGreeting include Pavlov::Interactor - arguments + + attribute :pavlov_options, Hash, default: {} + def authorized?; true; end def execute "OHAI, #{old_query :find_uppercase_name}" diff --git a/spec/pavlov/helpers_spec.rb b/spec/pavlov/helpers_spec.rb index 41f2fba..d2e737e 100644 --- a/spec/pavlov/helpers_spec.rb +++ b/spec/pavlov/helpers_spec.rb @@ -8,12 +8,12 @@ include Pavlov::Helpers def test - interactor :interactor_name, 'argument1', 'argument2' + old_interactor :interactor_name, 'argument1', 'argument2' end end instance = dummy_class.new - Pavlov.should_receive(:interactor) + Pavlov.should_receive(:old_interactor) .with(:interactor_name, 'argument1', 'argument2') instance.test @@ -29,12 +29,12 @@ def pavlov_options end def test - interactor :interactor_name, 'argument1', 'argument2' + old_interactor :interactor_name, 'argument1', 'argument2' end end instance = dummy_class.new - - Pavlov.should_receive(:interactor) + + Pavlov.should_receive(:old_interactor) .with(:interactor_name, 'argument1', 'argument2', hash) instance.test