lib/sequel-fixture.rb in sequel-fixture-0.0.3 vs lib/sequel-fixture.rb in sequel-fixture-2.0.0

- old
+ new

@@ -1,145 +1,197 @@ require "sequel" require "symbolmatrix" require "fast" require "sequel-fixture/version" +require "sequel-fixture/exceptions" +require "sequel-fixture/util" +require "sequel-fixture/table" -module Sequel +module Sequel; end - # Fixture managing class for Sequel - class Fixture - ## Class methods +class Sequel::Fixture + + # === Description + # Returns the current path to the fixtures folder + # + def self.path + @@path ||= "test/fixtures" + end + + + # === Description + # Set the current path of the fixtures folder + # + def self.path=(path) + @@path = path + end + + # === Description + # Initializes the fixture handler + # Accepts optionally a symbol as a reference to the fixture + # and a Sequel::Database connection + def initialize(fixture = nil, connection = nil, option_push = true) + @schema = {} + @data = {} - # Returns the current path to the fixtures folder - def self.path - @@path ||= "test/fixtures" - end + load(fixture) if fixture + @connection = connection if connection + push if fixture && connection && option_push + end + + + # === Description + # Loads the fixture files into this instance + # + def load(fixture) + raise LoadingFixtureIllegal, "A check has already been made, loading a different fixture is illegal" if @checked - ## Instance methods - - # Initializes the fixture handler - # Accepts optionally a symbol as a reference to the fixture - # and a Sequel::Database connection - def initialize fixture = nil, connection = nil, option_push = true - load fixture if fixture - - @connection = connection if connection - push if fixture && connection && option_push + Fast.dir("#{fixtures_path}/#{fixture}").files.to.symbols.each do |file| + @data ||= {} + @schema ||= {} + + file_data = SymbolMatrix.new "#{fixtures_path}/#{fixture}/#{file}.yaml" + + if file_data + @data[file] = Table.new(file_data[:data]) if file_data.key?(:data) + @schema[file] = file_data[:schema] if file_data.key?(:schema) + end end + end + + + # === Description + # Returns the current fixtures path where Sequel::Fixture looks for fixture folders + # + def fixtures_path + Sequel::Fixture.path + end + + + # === Description + # For enabling discovery of tables + # + def method_missing(key, *args) + return @data[key] if @data && @data.has_key?(key) + return super + end + + # === Description + # Returns the SymbolMatrix with the data referring to that table + # + def [](reference) + @data[reference] + end + + + # === Description + # Forces the check to pass. Dangerous! + # + def force_checked! + @checked = true + end + + + # === Description + # Assures that the tables are empty before proceeding + # + def check + return @checked if @checked # If already checked, it's alright + + raise MissingFixtureError, "No fixture has been loaded, nothing to check" unless @data.length > 0 + raise MissingConnectionError, "No connection has been provided, impossible to check" unless @connection - # Loads the fixture files into this instance - def load fixture - raise LoadingFixtureIllegal, "A check has already been made, loading a different fixture is illegal" if @checked - - Fast.dir("#{fixtures_path}/#{fixture}").files.to.symbols.each do |file| - @data ||= {} - @data[file] = SymbolMatrix.new "#{fixtures_path}/#{fixture}/#{file}.yaml" + @data.each_key do |table| + if @connection[table].count != 0 + raise TablesNotEmptyError, "Table '#{table}' is not empty, tables must be empty prior to testing" end end - - # Returns the current fixtures path where Sequel::Fixtures looks for fixture folders - def fixtures_path - Sequel::Fixture.path + return @checked = true + end + + + # === Description + # Initializes fixture schema and Inserts the fixture data into the corresponding + # tables + # + def push + check + + @schema.each do |table, matrix| + push_schema(table, matrix) end - # Returns the SymbolMatrix with the data referring to that table - def [] reference - @data[reference] + @data.each do |table_name, table_data| + table_data.rows.each do |values| + begin + @connection[table_name].insert(simplify(values.to_h)) + rescue MissingProcessedValueError => m + rollback + raise MissingProcessedValueError, "In record '#{values.to_h}' to be inserted into '#{table_name}', the processed value of field '#{m.field}' is missing, aborting." + rescue NoMethodError => e + raise IllegalFixtureFormat, "In record '#{values}', data must be formatted as arrays of hashes. Check 'data' section in '#{table_name}.yaml'" + end + end end - - # Method missing, for enabling discovery of tables - def method_missing s, *args - return @data[s] if @data && @data.has_key?(s) - return super + end + + + # === Description + # Create the schema in our DB connection based on the schema values + # + def push_schema(table, values) + ## Lets passively ignore the schema if the table already exists + return if @connection.table_exists?(table.to_sym) + + ## Find the primary key + pkey_data = nil + values.each do |column_def| + pkey_data = column_def if column_def["primary_key"] end - # Forces the check to pass. Dangerous! - def force_checked! - @checked = true + ## Create the table with the primary key + @connection.create_table(table) do + column(pkey_data["name"].to_sym, pkey_data["type"].to_sym) end - - # Assures that the tables are empty before proceeding - def check - return @checked if @checked # If already checked, it's alright - raise MissingFixtureError, "No fixture has been loaded, nothing to check" unless @data - raise MissingConnectionError, "No connection has been provided, impossible to check" unless @connection - - @data.each_key do |table| - raise TablesNotEmptyError, "The table '#{table}' is not empty, all tables should be empty prior to testing" if @connection[table].count != 0 + ## Add the rest of the columns + values.each do |column_def| + unless column_def["primary_key"] + @connection.alter_table(table) { add_column(column_def["name"].to_sym, column_def["type"].to_sym) } end - return @checked = true end - - # Inserts the fixture data into the corresponding tables - def push + end + + + # === Description + # Empties the tables, only if they were empty to begin with + # + def rollback + begin check - @data.each do |table, matrix| - matrix.each do |element, values| - begin - @connection[table].insert simplify values.to_hash - rescue MissingProcessedValueError => m - rollback - raise MissingProcessedValueError, "In record '#{element}' to be inserted into '#{table}', the processed value of field '#{m.field}' is missing, aborting" - end - end + @data.each_key do |table| + @connection[table].truncate end + rescue TablesNotEmptyError => e + raise RollbackIllegalError, "The tables weren't empty to begin with, rollback aborted." end - - # Empties the tables, only if they were empty to begin with - def rollback - begin - check - - @data.each_key do |table| - @connection[table].truncate - end - rescue TablesNotEmptyError => e - raise RollbackIllegalError, "The tables weren't empty to begin with, rollback aborted." - end - end - - attr_reader :connection - - # Sets the connection. Raises an ChangingConnectionIllegal exception if this fixture has already been checked - def connection= the_connection - raise ChangingConnectionIllegal, "A check has already been performed, changing the connection now is illegal" if @checked - @connection = the_connection - end - - attr_reader :data - - # Simplifies the hash in order to insert it into the database - # (Note: I'm well aware that this functionality belongs in a dependency) - def simplify the_hash - the_returned_hash = {} - the_hash.each do |key, value| - if value.is_a? Hash - unless value.has_key? :processed - raise MissingProcessedValueError.new "The processed value to insert into the db is missing from the field '#{key}', aborting", key - end - the_returned_hash[key] = value[:processed] - else - the_returned_hash[key] = value - end - end - return the_returned_hash - end + end - class TablesNotEmptyError < StandardError; end - class RollbackIllegalError < StandardError; end - class MissingFixtureError < StandardError; end - class MissingConnectionError < StandardError; end - class LoadingFixtureIllegal < StandardError; end - class ChangingConnectionIllegal < StandardError; end - class MissingProcessedValueError < StandardError - attr_accessor :field - def initialize message, field = nil - @field = field - super message - end + + # === Description + # Sets the connection. Raises an ChangingConnectionIllegal exception if this fixture has + # already been checked + # + def connection=(the_connection) + if @checked + raise ChangingConnectionIllegal, "Illegal to change connection after check has already been performed" end + @connection = the_connection end + + attr_reader :connection + attr_reader :data + attr_reader :schema end