From be350175786f7806f92c32bdfa203f26348f732b Mon Sep 17 00:00:00 2001 From: Ian White Date: Fri, 20 Aug 2010 16:17:17 +0100 Subject: [PATCH] Move some stuff out of config into matchers, expose just the pickle_ methods in Matchers --- lib/pickle/config.rb | 15 +-- lib/pickle/parser/matchers.rb | 72 +++++++------ lib/pickle/ref.rb | 6 +- spec/pickle/config_spec.rb | 103 +++++-------------- spec/pickle/parser/matchers_spec.rb | 152 +++++++++++++++++----------- 5 files changed, 160 insertions(+), 188 deletions(-) diff --git a/lib/pickle/config.rb b/lib/pickle/config.rb index 3ad497f6..e51735a4 100644 --- a/lib/pickle/config.rb +++ b/lib/pickle/config.rb @@ -39,7 +39,7 @@ def predicates # By default pickle_steps.rb just matches factory names that are a single word (with underscores). # using #factory_names lets you add extra expressions (for example with spaces instead of underscores) # - # config.names = ['admin user', 'site owner'] + # config.factories = ['admin user', 'site owner'] # # this will mean that #pickle_ref will match 'the admin user', and the resulting factory name # # will be 'admin_user' # @@ -47,15 +47,8 @@ def predicates # @return Array String def factories @factories ||= [] - @factories | aliases.keys end - - # pluralised versions of #names, no setter is available - # @return Array of string - def plural_factories - factories.map(&:pluralize) - end - + # alias a bunch of names to another name(s) # # config.alias 'admin', 'admin user', :to => 'external_lib_admin_user' # where External::Lib::Admin::User is one of your models @@ -75,10 +68,6 @@ class Mapping < Struct.new(:search, :replacement) def mappings @mapping ||= [] end - - def mapping_searches - mappings.map(&:search) - end # Usage: map 'me', 'myself', 'I', :to => 'user: "me"' def map(*args) diff --git a/lib/pickle/parser/matchers.rb b/lib/pickle/parser/matchers.rb index e39e1bd9..3c96091f 100644 --- a/lib/pickle/parser/matchers.rb +++ b/lib/pickle/parser/matchers.rb @@ -7,22 +7,37 @@ class Parser module Matchers attr_accessor :config - delegate :factories, :plural_factories, :predicates, :mapping_searches, :to => :config, :allow_nil => true + delegate :predicates, :to => :config, :allow_nil => true - def match_disjunction(*strings) - /(?:#{strings.compact.join('|')})/ + # generate an expression to capture a reference to a model + # @arguments to restrict the expression to the given factory names + # @return Regexp + def pickle_ref(*restrict_to) + /(#{match_pickle_ref(*restrict_to).source})/ end - def capture_disjunction(*strings) - /(#{strings.compact.join('|')})/ + # generate an expression to capture a plural factory name, such as 'users', 'admin users' + def pickle_plural + /(#{match_plural_factory.source})/ end - def match_quoted - /(?:"[^\"]*")/ + # generate an expression to capture a fields string suitable for pickle + def pickle_fields + /(#{match_fields.source})/ end - def capture_quoted - /(?:"([^\"]*)")/ + # generate an expression to capture a predicate, suitable for passing to Pickle::MakeMatcher#make_matcher + def pickle_predicate + /(#{match_predicate.source})/ + end + + protected + def match_disjunction(*strings) + /(?:#{strings.compact.join('|')})/ + end + + def match_quoted + /(?:"[^\"]*")/ end def match_factory @@ -58,39 +73,32 @@ def match_fields end def match_predicate - predicates ? match_disjunction(*(predicates + [match_quoted.source])) : match_quoted - end - - def match_model - /(?:(?:#{match_index.source} |#{match_prefix.source} )?#{match_factory.source}(?:(?: |: )#{match_quoted.source})?)/ - end - - def match_pickle_ref - /(?:#{mapping_searches ? match_disjunction(mapping_searches).source + '|' : ''}#{match_quoted.source}|#{match_model.source})/ + predicates ? match_disjunction(*(predicates + [match_quoted])) : match_quoted end - def capture_index - /(?:(?:the )?(#{match_index_word.source}))/ + def match_model(*restrict_to) + factory = restrict_to.any? ? match_disjunction(*restrict_to) : match_factory + /(?:(?:#{match_index.source} |#{match_prefix.source} )?#{factory.source}(?:(?: |: )#{match_quoted.source})?)/ end - def capture_factory - /(?:\b(\w\w+)\b)/ + def match_pickle_ref(*restrict_to) + if mappings && restrict_to.empty? + /(?:#{match_disjunction(mappings).source}|#{match_quoted.source}|#{match_model.source})/ + else + /(?:#{match_quoted.source}|#{match_model(*restrict_to).source})/ + end end - def capture_plural_factory - capture_name - end - - def capture_fields - /(?: (\w+\: .*))/ + def mappings + config && config.mappings.map(&:search) end - def capture_model - /(#{match_model.source})/ + def factories + config && (config.factories | config.aliases.keys) end - def capture_predicate - predicates ? capture_disjunction(*(predicates + [match_quoted.source])) : /(#{match_quoted.source})/ + def plural_factories + config && factories.map(&:pluralize) end end end diff --git a/lib/pickle/ref.rb b/lib/pickle/ref.rb index c50cbfef..9bec2e09 100644 --- a/lib/pickle/ref.rb +++ b/lib/pickle/ref.rb @@ -28,19 +28,19 @@ def parse_ref(orig) # parse and remove the index from the given string # @return the index or nil def parse_index!(string) - remove_from_and_return_1st_capture!(string, /^#{capture_index} /) + remove_from_and_return_1st_capture!(string, /^(?:the )?(#{match_index_word}) /) end # parse the factory name from the given string, remove the factory name and optional prefix # @return factory_name or nil def parse_factory!(string) - remove_from_and_return_1st_capture!(string, /^(?:#{match_prefix} )?#{capture_factory}/) + remove_from_and_return_1st_capture!(string, /^(?:#{match_prefix} )?(#{match_factory})/) end # parse the label, removing it if found # @return the label or nil def parse_label!(string) - remove_from_and_return_1st_capture!(string, /^(?: |: )?#{capture_quoted}/) + remove_from_and_return_1st_capture!(string, /^(?: |: )?(#{match_quoted})/).try(:gsub, '"', '') end def remove_from_and_return_1st_capture!(string, regexp) diff --git a/spec/pickle/config_spec.rb b/spec/pickle/config_spec.rb index 1a460aef..4ab3d61b 100644 --- a/spec/pickle/config_spec.rb +++ b/spec/pickle/config_spec.rb @@ -1,87 +1,34 @@ require 'spec_helper' describe Pickle::Config do - before do - @config = Pickle::Config.new + subject { Pickle::Config.new } + + describe "new" do + its(:adapters) { should == [:machinist, :factory_girl, :orm] } + its(:adapter_classes) { should == [Pickle::Adapter::Machinist, Pickle::Adapter::FactoryGirl, Pickle::Adapter::Orm] } + its(:predicates) { should be_empty } + its(:mappings) { should be_empty } + its(:factories) { should be_empty } + its(:plural_factories) { should be_empty } + its(:aliases) { should be_empty } + its(:labels) { should be_empty } end - it "#adapters should default to :machinist, :factory_girl, :orm" do - @config.adapters.should == [:machinist, :factory_girl, :orm] - end - - it "#adapter_classes should default to Adapter::Machinist, Adapter::FactoryGirl, Adapter::Orm" do - @config.adapter_classes.should == [Pickle::Adapter::Machinist, Pickle::Adapter::FactoryGirl, Pickle::Adapter::Orm] - end - - describe "setting adapters to [:machinist, SomeAdapter]" do - class SomeAdapter; end - - before do - @config.adapters = [:machinist, SomeAdapter] - end + describe "setting adapters to [:machinist, ]" do + let(:adapter_class) { Class.new(Object) } - it "#adapter_classes should be Adapter::Machinist, SomeAdapter" do - @config.adapter_classes.should == [Pickle::Adapter::Machinist, SomeAdapter] - end - end - - describe "#factories" do - it "should call adaptor.factories for each adaptor" do - Pickle::Adapter::Machinist.should_receive(:factories).and_return([]) - Pickle::Adapter::FactoryGirl.should_receive(:factories).and_return([]) - Pickle::Adapter::Orm.should_receive(:factories).and_return([]) - @config.factories - end - - it "should aggregate factories into a hash using factory name as key" do - Pickle::Adapter::Machinist.should_receive(:factories).and_return([@machinist = mock('machinist', :name => 'machinist')]) - Pickle::Adapter::FactoryGirl.should_receive(:factories).and_return([@factory_girl = mock('factory_girl', :name => 'factory_girl')]) - Pickle::Adapter::Orm.should_receive(:factories).and_return([@orm = mock('orm', :name => 'orm')]) - @config.factories.should == {'machinist' => @machinist, 'factory_girl' => @factory_girl, 'orm' => @orm} - end - - it "should give preference to adaptors first in the list" do - Pickle::Adapter::Machinist.should_receive(:factories).and_return([@machinist_one = mock('one', :name => 'one')]) - Pickle::Adapter::FactoryGirl.should_receive(:factories).and_return([@factory_girl_one = mock('one', :name => 'one'), @factory_girl_two = mock('two', :name => 'two')]) - Pickle::Adapter::Orm.should_receive(:factories).and_return([@orm_two = mock('two', :name => 'two'), @orm_three = mock('three', :name => 'three')]) - @config.factories.should == {'one' => @machinist_one, 'two' => @factory_girl_two, 'three' => @orm_three} - end - end - - it "#mappings should default to []" do - @config.mappings.should == [] - end - - describe '#predicates' do - it "should be list of all non object ? public instance methods + columns methods of Adapter.model_classes" do - class1 = mock('Class1', - :public_instance_methods => ['nope', 'foo?', 'bar?'], - :column_names => ['one', 'two'], - :const_get => ::ActiveRecord::Base::PickleAdapter - ) - class2 = mock('Class2', - :public_instance_methods => ['not', 'foo?', 'faz?'], - :column_names => ['two', 'three'], - :const_get => ::ActiveRecord::Base::PickleAdapter - ) - Pickle::Adapter.stub!(:model_classes).and_return([class1, class2]) - - @config.predicates.to_set.should == ['foo?', 'faz?', 'bar?', 'one', 'two', 'three'].to_set - end - - it "should be overridable" do - @config.predicates = %w(lame?) - @config.predicates.should == %w(lame?) + before { subject.adapters = [:machinist, adapter_class] } + + it "should have :adapter_classes [Pickle::Adapter::Machinist, ]" do + subject.adapter_classes.should == [Pickle::Adapter::Machinist, adapter_class] end end describe "#map 'foo', :to => 'faz'" do - before do - @config.map 'foo', :to => 'faz' - end + before { subject.map 'foo', :to => 'faz' } it "should create Mapping('foo', 'faz') mapping" do - @config.mappings.first.tap do |mapping| + subject.mappings.first.tap do |mapping| mapping.should be_kind_of Pickle::Config::Mapping mapping.search.should == 'foo' mapping.replacement.should == 'faz' @@ -90,19 +37,17 @@ class SomeAdapter; end end describe "#map 'foo', 'bar' :to => 'faz'" do - before do - @config.map 'foo', 'bar', :to => 'faz' - end + before { subject.map 'foo', 'bar', :to => 'faz' } it "should create 2 mappings" do - @config.mappings.first.should == Pickle::Config::Mapping.new('foo', 'faz') - @config.mappings.last.should == Pickle::Config::Mapping.new('bar', 'faz') + subject.mappings.first.should == Pickle::Config::Mapping.new('foo', 'faz') + subject.mappings.last.should == Pickle::Config::Mapping.new('bar', 'faz') end end it "#configure(&block) should execiute on self" do - @config.should_receive(:foo).with(:bar) - @config.configure do |c| + subject.should_receive(:foo).with(:bar) + subject.configure do |c| c.foo :bar end end diff --git a/spec/pickle/parser/matchers_spec.rb b/spec/pickle/parser/matchers_spec.rb index 067985c2..5dc30cde 100644 --- a/spec/pickle/parser/matchers_spec.rb +++ b/spec/pickle/parser/matchers_spec.rb @@ -28,85 +28,115 @@ def capture(expected, options = {}) end end - its(:match_quoted) { should match_all '""', '"gday mate"' } - its(:match_quoted) { should_not match_all '', "''", '"gday \" mate"' } - its(:capture_quoted) { should capture "gday mate", :from => '"gday mate"' } + describe "API" do + describe "#pickle_ref" do + subject { pickle_ref } + + it { should capture "the 1st user", :from => 'the 1st user' } + it { should capture 'another user: "fred"', :from => 'another user: "fred"' } + it { should_not capture anything, :from => 'the 1st admin user' } + + describe "('admin user')" do + subject { pickle_ref('admin user') } + + it { should capture "the 1st admin user", :from => 'the 1st admin user' } + it { should_not capture 'a product', :from => 'a product' } + end + + describe "when config.map 'I', :to => 'a user'" do + before { self.config = Pickle::Config.new{|c| c.map 'I', :to => 'a user' } } + it { should capture "I", :from => "I" } + it { should_not capture anything, :from => "1st I" } + end + + describe "when config.factories << 'admin user'" do + before { self.config = Pickle::Config.new{|c| c.factories << 'admin user' } } + it { should capture "the 1st admin user", :from => "the 1st admin user" } + end + end + + describe "#pickle_plural" do + + end + + describe "#pickle_fields" do + + end + + describe "#pickle_predicate" do + + end + end - its(:match_ordinal) { should match_all '1st', '2nd', '23rd', '104th' } - its(:match_ordinal) { should_not match_any '1', '2' } + describe "internals" do + its(:match_quoted) { should match_all '""', '"gday mate"' } + its(:match_quoted) { should_not match_all '', "''", '"gday \" mate"' } - its(:match_index) { should match_all 'first', 'last', '23rd', '104th' } - its(:match_index) { should_not match_any '1', '2', 'foo' } + its(:match_ordinal) { should match_all '1st', '2nd', '23rd', '104th' } + its(:match_ordinal) { should_not match_any '1', '2' } - its(:capture_index) { should capture "1st", :from => 'the 1st' } - its(:capture_index) { should capture "1st", :from => '1st' } - its(:capture_index) { should capture "first", :from => 'first' } + its(:match_index) { should match_all 'first', 'last', '23rd', '104th' } + its(:match_index) { should_not match_any '1', '2', 'foo' } - its(:match_field) { should match_all 'foo: "this is the life"', 'bar_man: "and so is this"', 'boolean: false', 'boolean: true', 'numeric: 10', 'numeric: 12.5', 'numeric: +10', 'numeric: +12.5', 'numeric: -10', 'numeric: -12.5', 'nil_field: nil' } - its(:match_field) { should_not match_any 'foo bar: "this aint workin"', '"foo": "Bar"', ":foo => bar" } + its(:match_field) { should match_all 'foo: "this is the life"', 'bar_man: "and so is this"', 'boolean: false', 'boolean: true', 'numeric: 10', 'numeric: 12.5', 'numeric: +10', 'numeric: +12.5', 'numeric: -10', 'numeric: -12.5', 'nil_field: nil' } + its(:match_field) { should_not match_any 'foo bar: "this aint workin"', '"foo": "Bar"', ":foo => bar" } - its(:match_fields) { should match_all 'foo: "bar"', 'foo: "bar", baz: "bah"' } - its(:match_fields) { should_not match_any 'foo bar: "baz"', 'email: "a", password: "b", and password_confirmation: "c"' } + its(:match_fields) { should match_all 'foo: "bar"', 'foo: "bar", baz: "bah"' } + its(:match_fields) { should_not match_any 'foo bar: "baz"', 'email: "a", password: "b", and password_confirmation: "c"' } - its(:match_model) { should match_all 'a user', 'another fast_car', 'the 23rd fast_car', '23rd fast_car', 'the user: "fred flinstone"' } - its(:match_model) { should_not match_any 'another fast car', 'a 1st fast_car', 'the 1st fast_car: "batmobile", ''an event created' } + its(:match_model) { should match_all 'a user', 'another fast_car', 'the 23rd fast_car', '23rd fast_car', 'the user: "fred flinstone"' } + its(:match_model) { should_not match_any 'another fast car', 'a 1st fast_car', 'the 1st fast_car: "batmobile", ''an event created' } - its(:match_predicate) { should match_all '"a super fun thing"', '"a_fun_thing"' } - its(:match_predicate) { should_not match_any 'a_fun_thing' } - - its(:capture_predicate) { should capture '"fred"', :from => '"fred"' } - - its(:match_factory) { should match_all 'user', 'fast_car', 'car' } - its(:match_factory) { should_not match_any 'admin users', 'faster car', 'event created' } + its(:match_predicate) { should match_all '"a super fun thing"', '"a_fun_thing"' } + its(:match_predicate) { should_not match_any 'a_fun_thing' } + + its(:match_factory) { should match_all 'user', 'fast_car', 'car' } + its(:match_factory) { should_not match_any 'admin users', 'faster car', 'event created' } - its(:match_plural_factory) { should match_all 'users', 'fast_cars', 'cars' } - its(:match_plural_factory) { should_not match_any 'admin users', 'faster cars', 'events created' } + its(:match_plural_factory) { should match_all 'users', 'fast_cars', 'cars' } + its(:match_plural_factory) { should_not match_any 'admin users', 'faster cars', 'events created' } - its(:match_pickle_ref) { should match_all 'a user', 'a user: "fred"', 'the 2nd user', 'the super_admin', '"fred"' } - its(:match_pickle_ref) { should_not match_any 'another person:', 'another person: ', 'an admin user', 'a 2nd user', '' } + its(:match_pickle_ref) { should match_all 'a user', 'a user: "fred"', 'the 2nd user', 'the super_admin', '"fred"' } + its(:match_pickle_ref) { should_not match_any 'another person:', 'another person: ', 'an admin user', 'a 2nd user', '' } - describe "with config.factories = ['admin user', 'funky thing']" do - before do - self.config = Pickle::Config.new {|c| c.factories = ["admin user", "funky thing"]} - end + describe "with config.factories = ['admin user', 'funky thing']" do + before do + self.config = Pickle::Config.new {|c| c.factories = ["admin user", "funky thing"]} + end - its(:match_factory) { should match_all 'admin user', 'funky thing' } - its(:match_factory) { should_not match_any 'admin users', 'funky thong' } + its(:match_factory) { should match_all 'admin user', 'funky thing' } + its(:match_factory) { should_not match_any 'admin users', 'funky thong' } - its(:match_plural_factory) { should match_all 'admin users', 'funky things' } - its(:match_plural_factory) { should_not match_any 'admin user', 'funky thong' } - end - - describe "with config.alias 'external admin', 'admin user', :to => 'external_library_admin_user'" do - before do - self.config = Pickle::Config.new {|c| c.alias 'external admin', 'admin user', :to => 'external_library_admin_user'} + its(:match_plural_factory) { should match_all 'admin users', 'funky things' } + its(:match_plural_factory) { should_not match_any 'admin user', 'funky thong' } end + + describe "with config.alias 'external admin', 'admin user', :to => 'external_library_admin_user'" do + before do + self.config = Pickle::Config.new {|c| c.alias 'external admin', 'admin user', :to => 'external_library_admin_user'} + end - its(:match_factory) { should match_all 'external admin', 'admin user' } - its(:match_factory) { should_not match_any 'external library admin user' } - - its(:match_plural_factory) { should match_all 'admin users', 'external admins' } - its(:match_plural_factory) { should_not match_any 'external library admin user' } - end + its(:match_factory) { should match_all 'external admin', 'admin user' } + its(:match_factory) { should_not match_any 'external library admin user' } + end - describe "with config.predicates = ['amazingly large', 'empty']" do - before do - self.config = Pickle::Config.new {|c| c.predicates = ['amazingly large', 'empty']} - end + describe "with config.predicates = ['amazingly large', 'empty']" do + before do + self.config = Pickle::Config.new {|c| c.predicates = ['amazingly large', 'empty']} + end - its(:match_predicate) { should match_all 'amazingly large', 'empty', '"any"' } - its(:match_predicate) { should_not match_all 'amazingly "large"', 'empty?', 'any' } - its(:capture_predicate) { should capture "amazingly large", :from => 'amazingly large' } - its(:capture_predicate) { should capture '"small"', :from => '"small"' } - end - - describe "with config.map 'me', 'myself', 'I', :to => 'user \"me\"'" do - before do - self.config = Pickle::Config.new {|c| c.map 'me', 'myself', 'I', 'skin coloured bag of bones and meat', :to => 'user "me"' } + its(:match_predicate) { should match_all 'amazingly large', 'empty', '"any"' } + its(:match_predicate) { should_not match_all 'amazingly "large"', 'empty?', 'any' } end + + describe "with config.map 'me', 'myself', 'I', :to => 'user \"me\"'" do + before do + self.config = Pickle::Config.new {|c| c.map 'me', 'myself', 'I', 'skin coloured bag of bones and meat', :to => 'user "me"' } + end - its(:match_pickle_ref) { should match_all 'myself', 'me', 'I', 'user: "me"', 'skin coloured bag of bones and meat' } - its(:match_pickle_ref) { should_not match_any '1st I', 'I "me"', '1st skin coloured bag of bones and meat' } + its(:match_pickle_ref) { should match_all 'myself', 'me', 'I', 'user: "me"', 'skin coloured bag of bones and meat' } + its(:match_pickle_ref) { should_not match_any '1st I', 'I "me"', '1st skin coloured bag of bones and meat' } + end end end \ No newline at end of file