require "rake" require "magic_lamp/engine" require "magic_lamp/constants" require "magic_lamp/callbacks" require "magic_lamp/configuration" require "magic_lamp/defaults_manager" require "magic_lamp/fixture_creator" require "magic_lamp/render_catcher" require "tasks/lint_task" require "tasks/fixture_names_task" module MagicLamp class << self attr_accessor :registered_fixtures, :configuration def register_fixture(options = {}, &render_block) raise_missing_block_error(render_block, __method__) options[:controller] ||= ::ApplicationController options[:namespace] ||= options[:controller].controller_name options[:extend] = Array(options[:extend]) options[:render_block] = render_block fixture_name = namespaced_fixture_name_or_raise(options) if registered?(fixture_name) raise AlreadyRegisteredFixtureError, "a fixture called '#{fixture_name}' has already been registered" end registered_fixtures[fixture_name] = options end REGISTER_FIXTURE_ALIASES.each do |method_name| alias_method method_name, :register_fixture end def configure(&block) raise_missing_block_error(block, __method__) block.call(configuration) end def define(options = {}, &block) raise_missing_block_error(block, __method__) defaults_manager = DefaultsManager.new(configuration, options) defaults_manager.instance_eval(&block) defaults_manager end def registered?(fixture_name) registered_fixtures.key?(fixture_name) end def load_config FactoryGirl.reload if defined?(FactoryGirl) self.configuration = Configuration.new load_all(config_files) end def lint_config self.registered_fixtures = {} errors = {} add_error_if_error(errors, :config_file_load) { load_config } add_callback_error_if_error(errors, :before_each) add_callback_error_if_error(errors, :after_each) errors end def lint_fixtures self.registered_fixtures = {} file_errors = {} fixture_errors = {} lamp_files.each do |lamp_file| add_error_if_error(file_errors, lamp_file) { load lamp_file } end registered_fixtures.each do |fixture_name, fixture_info| begin generate_fixture(fixture_name) rescue => e fixture_errors[fixture_name] = fixture_info.merge(error: compose_error(e)) end end { files: file_errors, fixtures: fixture_errors } end def load_lamp_files self.registered_fixtures = {} load_config load_all(lamp_files) end def generate_fixture(fixture_name) unless registered?(fixture_name) raise UnregisteredFixtureError, "'#{fixture_name}' is not a registered fixture" end controller_class, block, extensions = registered_fixtures[fixture_name].values_at(:controller, :render_block, :extend) FixtureCreator.new(configuration).generate_template(controller_class, extensions, &block) end def generate_all_fixtures load_lamp_files registered_fixtures.keys.each_with_object({}) do |fixture_name, fixtures| fixtures[fixture_name] = generate_fixture(fixture_name) end end private def compose_error(error) name = "#{error.class}: #{error.message}" ([name] + error.backtrace).join("\n\s\s\s\s") end def add_error_if_error(error_hash, key, &block) block.call rescue => e error_hash[key] = compose_error(e) end def add_callback_error_if_error(error_hash, callback_type) add_error_if_error(error_hash, callback_type) do callback = configuration.send("#{callback_type}_proc") Object.new.instance_eval(&callback) if callback end end def namespaced_fixture_name_or_raise(options) fixture_name = options.delete(:name) controller_class, render_block = options.values_at(:controller, :render_block) fixture_name = fixture_name_or_raise(fixture_name, controller_class, render_block) namespace_fixture_name(fixture_name, options[:namespace]) end def namespace_fixture_name(fixture_name, namespace) namespace_without_application = strip_application(namespace) full_name = compose_full_name(namespace_without_application, fixture_name) full_name.split(FORWARD_SLASH).each do |namespace_piece| namespace_piece_doubled = [namespace_piece, namespace_piece].join(FORWARD_SLASH) full_name.gsub!(namespace_piece_doubled, namespace_piece) end full_name end def strip_application(namespace) namespace.gsub(APPLICATION_MATCHER, EMPTY_STRING) end def compose_full_name(namespace, fixture_name) [namespace, fixture_name].select(&:present?).join(FORWARD_SLASH) end def fixture_name_or_raise(fixture_name, controller_class, block) if fixture_name.nil? && configuration.infer_names default_fixture_name(controller_class, block) elsif fixture_name.nil? raise ArgumentError, "You must specify a name since `infer_names` is configured to `false`" else fixture_name end end def raise_missing_block_error(block, method_name) if block.nil? raise ArgumentError, "MagicLamp##{method_name} requires a block" end end def config_files Dir[path.join(STARS, "magic#{LAMP}_config.rb")] end def lamp_files Dir[path.join(STARS, "*#{LAMP}.rb")] end def default_fixture_name(controller_class, block) first_arg = first_render_arg(block) fixture_name = template_name(first_arg).to_s if fixture_name.blank? raise AmbiguousFixtureNameError, "Unable to infer fixture name" end fixture_name end def first_render_arg(block) render_catcher = RenderCatcher.new(configuration) render_catcher.first_render_argument(&block) end def template_name(render_arg) if render_arg.is_a?(Hash) render_arg[:template] || render_arg[:partial] else render_arg end end def path Rails.root.join("{spec,test}") end def load_all(files) files.each { |file| load file } end end end MagicLamp.configuration = MagicLamp::Configuration.new