require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') require "om" describe "OM::XML::Term::Builder" do before(:each) do @test_terminology_builder = OM::XML::Terminology::Builder.new do |t| t.fruit_trees { t.citrus(:attributes=>{"citric_acid"=>"true"}, :index_as=>[:facetable]) { t.randomness } t.stone_fruit(:path=>"prunus", :attributes=>{:genus=>"Prunus"}) t.peach(:ref=>[:fruit_trees, :stone_fruit], :attributes=>{:subgenus=>"Amygdalus", :species=>"Prunus persica"}) t.nectarine(:ref=>[:fruit_trees, :peach], :attributes=>{:cultivar=>"nectarine"}) t.almond(:ref=>[:fruit_trees, :peach], :attributes=>{:species=>"Prunus dulcis"}) } t.coconut(:ref=>:pineapple) t.banana(:ref=>:coconut) t.pineapple(:ref=>:banana) end @citrus = @test_terminology_builder.retrieve_term_builder(:fruit_trees, :citrus) @stone_fruit = @test_terminology_builder.retrieve_term_builder(:fruit_trees, :stone_fruit) @peach = @test_terminology_builder.retrieve_term_builder(:fruit_trees, :peach) @nectarine = @test_terminology_builder.retrieve_term_builder(:fruit_trees, :nectarine) @almond = @test_terminology_builder.retrieve_term_builder(:fruit_trees, :almond) @pineapple = @test_terminology_builder.retrieve_term_builder(:pineapple) end before(:each) do @test_builder = OM::XML::Term::Builder.new("term1") @test_builder_2 = OM::XML::Term::Builder.new("term2") end describe '#new' do it "should set terminology_builder attribute if provided" do mock_terminology_builder = mock("TerminologyBuilder") OM::XML::Term::Builder.new("term1", mock_terminology_builder).terminology_builder.should == mock_terminology_builder end end describe "configuration methods" do it "should set the corresponding .settings value return the mapping object" do [:path, :index_as, :required, :data_type, :variant_of, :path, :attributes, :default_content_path].each do |method_name| @test_builder.send(method_name, "#{method_name.to_s}foo").should == @test_builder @test_builder.settings[method_name].should == "#{method_name.to_s}foo" end end it "should be chainable" do test_builder = OM::XML::Term::Builder.new("chainableTerm").index_as(:facetable, :searchable, :sortable, :displayable).required(true).data_type(:text) resulting_settings = test_builder.settings resulting_settings[:index_as].should == [:facetable, :searchable, :sortable, :displayable] resulting_settings[:required].should == true resulting_settings[:data_type].should == :text end end describe "settings" do describe "defaults" do it "should be set" do @test_builder.settings[:required].should == false @test_builder.settings[:data_type].should == :string @test_builder.settings[:variant_of].should be_nil @test_builder.settings[:attributes].should be_nil @test_builder.settings[:default_content_path].should be_nil end end end describe ".add_child" do it "should insert the given Term Builder into the current Term Builder's children" do @test_builder.add_child(@test_builder_2) @test_builder.children[@test_builder_2.name].should == @test_builder_2 @test_builder.ancestors.should include(@test_builder_2) end end describe ".retrieve_child" do it "should fetch the child identified by the given name" do @test_builder.add_child(@test_builder_2) @test_builder.retrieve_child(@test_builder_2.name).should == @test_builder.children[@test_builder_2.name] end end describe ".children" do it "should return a hash of Term Builders that are the children of the current object, indexed by name" do @test_builder.add_child(@test_builder_2) @test_builder.children[@test_builder_2.name].should == @test_builder_2 end end describe ".build" do it "should build a Term with the given settings and generate its xpath values" do test_builder = OM::XML::Term::Builder.new("requiredTextFacet").index_as([:facetable, :searchable, :sortable, :displayable]).required(true).data_type(:text) result = test_builder.build result.should be_instance_of OM::XML::Term result.index_as.should == [:facetable, :searchable, :sortable, :displayable] result.required.should == true result.data_type.should == :text result.xpath.should == OM::XML::TermXpathGenerator.generate_absolute_xpath(result) result.xpath_constrained.should == OM::XML::TermXpathGenerator.generate_constrained_xpath(result) result.xpath_relative.should == OM::XML::TermXpathGenerator.generate_relative_xpath(result) end it "should create proxy terms if :proxy is set" do test_builder = OM::XML::Term::Builder.new("my_proxy").proxy([:foo, :bar]) result = test_builder.build result.should be_kind_of OM::XML::NamedTermProxy end it "should set path to match name if it is empty" do @test_builder.settings[:path].should be_nil @test_builder.build.path.should == @test_builder.name.to_s end it "should work recursively, calling .build on any of its children" do OM::XML::Term.any_instance.stubs(:generate_xpath_queries!) built_child1 = OM::XML::Term.new("child1") built_child2 = OM::XML::Term.new("child2") mock1 = mock("Builder1", :build => built_child1 ) mock2 = mock("Builder2", :build => built_child2 ) mock1.stubs(:name).returns("child1") mock2.stubs(:name).returns("child2") @test_builder.children = {:mock1=>mock1, :mock2=>mock2} result = @test_builder.build result.children[:child1].should == built_child1 result.children[:child2].should == built_child2 result.children.length.should == 2 end end describe ".lookup_refs" do it "should return an empty array if no refs are declared" do @test_builder.lookup_refs.should == [] end it "should should look up the referenced TermBuilder from the terminology_builder" do @peach.lookup_refs.should == [@stone_fruit] end it "should support recursive refs" do @almond.lookup_refs.should == [@peach, @stone_fruit] end it "should raise an error if the TermBuilder does not have a reference to a terminology builder" do lambda { OM::XML::Term::Builder.new("referrer").ref("bongos").lookup_refs }.should raise_error(StandardError,"Cannot perform lookup_ref for the referrer builder. It doesn't have a reference to any terminology builder") end it "should raise an error if the referece points to a nonexistent term builder" do tb = OM::XML::Term::Builder.new("mork",@test_terminology_builder).ref(:characters, :aliens) lambda { tb.lookup_refs }.should raise_error(OM::XML::Terminology::BadPointerError,"#{tb.name} refers to a Term Builder that doesn't exist. The bad pointer is [:characters, :aliens]") end it "should raise an error with informative error when given circular references" do lambda { @pineapple.lookup_refs }.should raise_error(OM::XML::Terminology::CircularReferenceError,"Circular reference in Terminology: :pineapple => :banana => :coconut => :pineapple") end end describe ".resolve_refs!" do it "should do nothing if settings don't include a :ref" do settings_pre = @test_builder.settings children_pre = @test_builder.children @test_builder.resolve_refs! @test_builder.settings.should == settings_pre @test_builder.children.should == children_pre end it "should should look up the referenced TermBuilder, use its settings and duplicate its children without changing the name" do term_builder = OM::XML::Term::Builder.new("orange",@test_terminology_builder).ref(:fruit_trees, :citrus) term_builder.resolve_refs! # Make sure children and settings were copied term_builder.settings.should == @citrus.settings.merge(:path=>"citrus") term_builder.children.should == @citrus.children # Make sure name and parent of both the term_builder and its target were left alone term_builder.name.should == :orange @citrus.name.should == :citrus end it "should set path based on the ref's path if set" do [@peach,@almond].each { |x| x.resolve_refs! } @peach.settings[:path].should == "prunus" @almond.settings[:path].should == "prunus" end it "should set path based on the first ref's name if no path is set" do orange_builder = OM::XML::Term::Builder.new("orange",@test_terminology_builder).ref(:fruit_trees, :citrus) orange_builder.resolve_refs! orange_builder.settings[:path].should == "citrus" end # It should not be a problem if multiple TermBuilders refer to the same child TermBuilder since the parent-child relationship is set up after calling TermBuilder.build it "should result in clean trees of Terms after building" it "should preserve any extra settings specific to this builder (for variant terms)" do tb = OM::XML::Term::Builder.new("orange",@test_terminology_builder).ref(:fruit_trees, :citrus).attributes(:color=>"orange").required(true) tb.resolve_refs! tb.settings.should == {:path=>"citrus", :attributes=>{"citric_acid"=>"true", :color=>"orange"}, :required=>true, :data_type=>:string, :index_as=>[:facetable]} end it "should aggregate all settings from refs, combining them with a cascading approach" do @almond.resolve_refs! @almond.settings[:attributes].should == {:genus=>"Prunus",:subgenus=>"Amygdalus", :species=>"Prunus dulcis"} end end end