From 82f277fb7adadc9700b6fcf1b00797a3ca7408b4 Mon Sep 17 00:00:00 2001 From: "M@ McCray" Date: Wed, 24 Oct 2007 13:05:10 -0500 Subject: [PATCH] Added support for alternate (cleaner) syntax --- Rakefile | 14 ++ ReadMe | 26 +++ lib/doozer.rb | 1 + lib/doozer/alt_definition.rb | 83 ++++++++ lib/doozer/definition.rb | 2 +- test/fixtures/doozers/users_and_comments.rb | 24 ++- test/fixtures/nodes.yml | 2 +- test/fixtures/users.yml | 6 +- test/unit/alt_definition_test.rb | 315 ++++++++++++++++++++++++++++ 9 files changed, 460 insertions(+), 13 deletions(-) create mode 100644 lib/doozer/alt_definition.rb create mode 100644 test/unit/alt_definition_test.rb diff --git a/Rakefile b/Rakefile index 4f2d77f..0fb0fa3 100644 --- a/Rakefile +++ b/Rakefile @@ -20,6 +20,20 @@ end desc 'Run the default tasks' task :default => :test + +require 'spec/rake/spectask' + +desc "Run all examples" +Spec::Rake::SpecTask.new('spec') do |t| + t.spec_files = FileList['specs/**/*.rb'] +end + +desc "Generate specdocs for examples for inclusion in RDoc" +Spec::Rake::SpecTask.new('specdoc') do |t| + t.spec_files = FileList['specs/**/*.rb'] + t.spec_opts = ["--format", "rdoc:EXAMPLES.rd"] +end + Rake::TestTask.new do |t| t.libs << "lib" t.test_files = FileList['test/*/*test.rb'] diff --git a/ReadMe b/ReadMe index 0a89da2..dd98a2c 100644 --- a/ReadMe +++ b/ReadMe @@ -3,3 +3,29 @@ Doozer is a simple fixture generation library/plugin that handles all the annoying id management, and association building, allowing you to focus on crafting meaningful test fixtures. See docs/readme.html for more... + +# Notes + +Use a syntax more like this: (?) + + +doozer :bob, as=>:nancy do + + fields { + id + name + age + stuff = "STUFF" + created_on = Time.now + } + + children :belong_to + + on :generate { |atts| + atts.user_id = 10 + } + +end + + + diff --git a/lib/doozer.rb b/lib/doozer.rb index 84f1cd8..a8baa63 100644 --- a/lib/doozer.rb +++ b/lib/doozer.rb @@ -42,5 +42,6 @@ require 'active_support' require 'yaml' require 'doozer/definition' +require 'doozer/alt_definition' require 'doozer/builder' require 'doozer/version' diff --git a/lib/doozer/alt_definition.rb b/lib/doozer/alt_definition.rb new file mode 100644 index 0000000..f9dcfeb --- /dev/null +++ b/lib/doozer/alt_definition.rb @@ -0,0 +1,83 @@ +require 'active_support' + +module Kernel + def as + :as + end +end + +def doozer(name, opts={}, &block) + # Load up the settings from the alternate syntax + altdef = Doozer::AltDefinition.new(name, opts) + altdef.instance_eval(&block) if block_given? + + # Redefine in official definition format... kinda + Doozer.define( name, opts ) + obj_list = Doozer.object_list[name] + obj_list[:field_order] = altdef.field_definitions.field_list unless altdef.field_definitions.field_list.empty? + obj_list[:default_values] = altdef.field_definitions.field_defaults + obj_list[:block_affects] = altdef.block_affects unless altdef.block_affects.nil? + obj_list[:before_generate] = altdef.event_handlers[:generate] if altdef.event_handlers.has_key?(:generate) + obj_list[:target] = altdef.target_name unless altdef.target_name.nil? +end + +module Doozer + class AltDefinition + attr_reader :name, :options, :field_definitions, :block_affects, :event_handlers, :target_name + + def initialize(name, opts={}) + @name = name + @options = opts + @field_definitions = CleanFields.new + @block_affects = nil + @target_name = nil + @event_handlers = {} + end + + def fields(&block) + @field_definitions.instance_eval(&block) + end + + def children(action) + if action == :belong_to + @block_affects ||= [] + @block_affects << {:child=>:belongs_to} + elsif action == :act_as_tree or action == :parent_id + @block_affects ||= [] + @block_affects << {:child=>:parent_id} + end + end + + def parent(action) + if action == :belongs_to + @block_affects ||= [] + @block_affects << {:parent=>:belongs_to} + end + end + + def target(target_name) + @target_name = target_name + end + + def on(key, &block) + @event_handlers[key] = block if block_given? + end + + end + + class CleanFields + attr_reader :field_list, :field_defaults + def initialize + @field_list = [] + @field_defaults = {} + end + def id(*args) method_missing(:id, *args); end + def name(*args) method_missing(:name, *args); end + def type(*args) method_missing(:type, *args); end + def method_missing(name, *args) + name = name.id2name + @field_list << name.to_sym + @field_defaults[name.to_sym] = args[0] if args.length > 0 + end + end +end \ No newline at end of file diff --git a/lib/doozer/definition.rb b/lib/doozer/definition.rb index 38122ae..8df3327 100644 --- a/lib/doozer/definition.rb +++ b/lib/doozer/definition.rb @@ -100,7 +100,7 @@ module Doozer end end - # GENERATED MACROS CALL THESE TO GENERATE/ACCESS FIXTURE DATA + # GENERATED MACROS CALL THESE METHODS TO GENERATE/ACCESS FIXTURE DATA def add_object(obj_type, *params, &block) #:nodoc: diff --git a/test/fixtures/doozers/users_and_comments.rb b/test/fixtures/doozers/users_and_comments.rb index bf7349f..585d3b7 100644 --- a/test/fixtures/doozers/users_and_comments.rb +++ b/test/fixtures/doozers/users_and_comments.rb @@ -1,17 +1,25 @@ require 'doozer' -Doozer.define :user do - field_order :name, :email, :password - defaults :password=>'???', :created_on=>Time.now.to_s(:db) - before_generate do |atts| +doozer :user do + fields { + name + email + password "???" + created_on Time.now.to_s(:db) + } + on :generate do |atts| atts[:login] = Doozer.object_list[:user][:row_names][atts[:id]-1].to_s unless atts.has_key?(:login) end end -Doozer.define :comment do - field_order :title, :body - defaults :created_on=>Time.now.to_s(:db), :author=>'unknown' - before_generate do |atts| +doozer :comment do + fields { + title + body + author 'unknown' + created_on Time.now.to_s(:db) + } + on :generate do |atts| atts[:author] = users(atts[:user_id])[:name] if att.has_key? :user_id end end diff --git a/test/fixtures/nodes.yml b/test/fixtures/nodes.yml index 73d3f2f..d77799f 100644 --- a/test/fixtures/nodes.yml +++ b/test/fixtures/nodes.yml @@ -1,7 +1,7 @@ # This fixture was generated with a Doozer. You can find it's definition # script under test/fixtures/doozers/ # -# Generated on July 31, 2007 14:32 +# Generated on October 24, 2007 13:03 node_1: name: Root Node diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml index 698b52a..c79b3fb 100644 --- a/test/fixtures/users.yml +++ b/test/fixtures/users.yml @@ -1,19 +1,19 @@ # This fixture was generated with a Doozer. You can find it's definition # script under test/fixtures/doozers/ # -# Generated on July 31, 2007 14:32 +# Generated on October 24, 2007 13:03 bob: + created_on: 2007-10-24 13:03:30 name: Bob - created_on: 2007-07-31 14:32:36 id: 1 password: Bobo login: bob email: bob@yahoo.com user_2: + created_on: 2007-10-24 13:03:30 name: Other - created_on: 2007-07-31 14:32:36 id: 2 password: ??? login: user_2 diff --git a/test/unit/alt_definition_test.rb b/test/unit/alt_definition_test.rb new file mode 100644 index 0000000..1c936cb --- /dev/null +++ b/test/unit/alt_definition_test.rb @@ -0,0 +1,315 @@ +require File.dirname(__FILE__) + '/../test_helper' +require 'doozer' +require 'doozer/alt_definition' + +require 'pp' + +class AltDefinitionTest < Test::Unit::TestCase + + def setup + Doozer.reset! + end + + should "generate fixture definition data structure" do # --------------- + + doozer :user + + doozer :page do + fields { # Defaults + title 'My Title' + content + author + } + end + + expected_structure = { + :user=>{ + :target=>:user, + :row_names=>[], + :data_rows=>{}, + :default_values=>{}, + :block_affects=>[{:child=>:belongs_to}] + }, + :page=>{ + :target=>:page, + :default_values=>{:title=>"My Title"}, + :block_affects=>[{:child=>:belongs_to}], + :field_order=>[:title, :content, :author], + :row_names=>[], + :data_rows=>{} + } + } + + assert_equal expected_structure, Doozer.object_list + end + + should "generate helper methods" do # ---------------------------------- + + doozer :page + + assert_not_nil defined?(page) + assert_not_nil defined?(page_id) + assert_not_nil defined?(page_name) + assert_not_nil defined?(pages) + + doozer :person + + assert_not_nil defined?(person) + assert_not_nil defined?(person_id) + assert_not_nil defined?(person_name) + assert_not_nil defined?(people) + end + + should "return next object id" do # ------------------------------------ + + doozer :user + + assert_equal 1, Doozer.next_object_id( :user ) + user :bob, :name=>'Bob' + assert_equal 2, Doozer.next_object_id( :user ) + end + + should "add objects to data pool from named arguments" do # ------------ + + doozer :user + + user :bob, :name=>'Bob' + assert_equal 1, user_id(:bob) + assert_equal "Bob", users(:bob)[:name] + + user :name=>'Matt' + user :name=>'Dan' + + assert_equal 3, user_id(:user_3) + assert_equal 'Dan', users(:user_3)[:name] + end + + should "add objects to data pool from positional arguments" do # ------- + + doozer :user do + fields { name } + end + + user :bob, 'Bob' + assert_equal 1, user_id(:bob) + assert_equal "Bob", users(:bob)[:name] + + user :matt, 'Matt' + user 'Dan' + + assert_equal 3, user_id(:user_3) + assert_equal 'Dan', users(:user_3)[:name] + end + + should "add default values to data objects" do # ----------------------- + created_on_time = Time.now + + doozer :page do + fields { # DEFAULTS: + title 'New Title' + author 'System' + content 'Content' + created_on created_on_time + slug 'Slugline' + } + end + + page "A beginning" + page + page "Stuff", "Junk", "And more!" + + assert_equal 3, Doozer.object_list[:page][:data_rows].keys.length + assert_equal created_on_time, pages(:page_1)[:created_on] + assert_equal "Slugline", pages(:page_1)[:slug] + assert_equal "A beginning", pages(:page_1)[:title] + end + + should "allow multiple updates to a single named fixture" do # --------- + + doozer :car do + fields { + name 'CAR' + type 'TYPE' + year 'YEAR' + } + end + + car :fast + assert_equal 1, cars(:fast)[:id] + assert_equal 'CAR', cars(:fast)[:name] + assert_equal 'TYPE', cars(:fast)[:type] + assert_equal 'YEAR', cars(:fast)[:year] + + car :fast, 'Corvette' + car :fast, :type=>'Sportscar' + car :fast, :year=>2002 + + assert_equal 'Corvette', cars(:fast)[:name] + assert_equal 'Sportscar', cars(:fast)[:type] + assert_equal 2002, cars(:fast)[:year] + + + # Last one in should win + car :fast, 'Tiburon' + assert_equal 'Tiburon', cars(:fast)[:name] + + # Positional params 'beat' named params + car :fast, 'Viper', :name=>'Porche' + assert_equal 'Viper', cars(:fast)[:name] + + # Shouldn't wind up with multiple :fast keys in object list + assert_equal [:fast], Doozer.object_list[:car][:row_names] + end + + should "be able to override the target AR class" do # ------------------ + + doozer :page do + target 'Comatose::Page' + end + + assert_equal 'Comatose::Page', Doozer.object_list[:page][:target] + + end + + should "support block association definition" do # --------------------- + + doozer :node do + fields { name } + children :act_as_tree + end + + assert_equal Doozer.object_list[:node][:block_affects], [{:child=>:parent_id}] + end + + should "correctly nest fixtures" do # ---------------------------------- + + doozer :node do + fields { name } + children :act_as_tree + end + + node "Root Node" do + node "First Child" do + assert_equal 2, Doozer.object_stack.length + node "Sub Child 1" + node "Sub Child 2" + end + node "Second Child" + end + + assert_equal 1, nodes(:node_2)[:parent_id] + assert_equal 2, nodes(:node_3)[:parent_id] + assert_equal 2, nodes(:node_4)[:parent_id] + end + + should "allow macro aliases" do # -------------------------------------- + doozer :node, as=>:n do + fields { name } + children :act_as_tree + end + + assert_not_nil defined?(n) + assert_not_nil defined?(n_id) + assert_not_nil defined?(ns) + + n "Root Node" do + n "First Child" do + n "Sub Child 1" + n "Sub Child 2" + end + n "Second Child" + end + + assert_equal 1, ns(:node_2)[:parent_id] + assert_equal 2, ns(:node_3)[:parent_id] + assert_equal 2, ns(:node_4)[:parent_id] + end + + should "allow alternate block syntax" do # ----------------------------- + doozer(:node, as=>:n){ + fields {name} + children :act_as_tree + } + + n("Root Node") { + n("First Child") { + n "Sub Child 1" + n "Sub Child 2" + } + n "Second Child" + } + + assert_equal 1, ns(:node_2)[:parent_id] + assert_equal 2, ns(:node_3)[:parent_id] + assert_equal 2, ns(:node_4)[:parent_id] + + expected_row_data = { + :node_1=>{:name=>"Root Node", :id=>1}, + :node_2=>{:parent_id=>1, :name=>"First Child", :id=>2}, + :node_3=>{:parent_id=>2, :name=>"Sub Child 1", :id=>3}, + :node_4=>{:parent_id=>2, :name=>"Sub Child 2", :id=>4}, + :node_5=>{:parent_id=>1, :name=>"Second Child", :id=>5} + } + + assert_equal expected_row_data, Doozer.object_list[:node][:data_rows] + end + + should "allow `belongs_to` relationships" do # -------------------------- + + doozer :page do + fields { title } + parent :belongs_to + end + doozer :user do + fields { name; email } + children :belong_to + end + doozer :comment do + fields { body } + # Use the default block_affects, which should be `block_affects :child=>:belongs_to` + end + + # Data definitiions: + + page :welcome, 'Welcome' do + user :matt, 'Matt' do + comment :matt_first_comment, 'The beginning' + end + end + + assert_equal user_id(:matt), pages(:welcome)[:user_id] + assert_equal user_id(:matt), comments(:matt_first_comment)[:user_id] + end + + should "remove macro names on `reset!`" do # --------------------------- + doozer :page + + assert_not_nil defined?(page) + assert_not_nil defined?(page_id) + assert_not_nil defined?(page_name) + assert_not_nil defined?(pages) + + Doozer.reset! + + assert_nil defined?(page) + assert_nil defined?(page_id) + assert_nil defined?(page_name) + assert_nil defined?(pages) + end + + should "allow access to the fixture name and data and from `ID`" do # -- + doozer :page do + fields { title } + end + + page :one, "Page One" + page :two, "Page Two" + page :three, "Page Three" + + assert_equal :one, page_name( page_id(:one) ) + assert_equal :two, page_name( page_id(:two) ) + + assert_equal 'Page One', pages( page_id(:one) )[:title] + assert_equal 'Page Three', pages( page_id(:three) )[:title] + end +end -- 2.11.4.GIT