From 82b0e30359ba66e8996f11c99ad46bbd3ae2bd7b Mon Sep 17 00:00:00 2001 From: Alejandro Dustet Date: Fri, 12 Jul 2019 16:56:01 -0400 Subject: [PATCH] feature Add file_fixture helper to syntax methods Why: This is to add simple support to files on factories. This is the original [issue](https://github.com/thoughtbot/factory_bot/issues/1282). What: It is a common behavior to interact with files when creating factories, and it is used heavily, as demonstrated with the many examples in the issue, this is a first draft and I would love to see more use-cases to add to the acceptance test. For the time being we have at least the base case added. --- lib/factory_bot.rb | 1 + lib/factory_bot/errors.rb | 2 ++ lib/factory_bot/file_loader.rb | 39 +++++++++++++++++++++++++ lib/factory_bot/syntax/methods.rb | 14 +++++++++ spec/acceptance/file_attributes_spec.rb | 20 +++++++++++++ spec/factory_bot/file_loader_spec.rb | 21 +++++++++++++ spec/support/text_file.txt | 1 + 7 files changed, 98 insertions(+) create mode 100644 lib/factory_bot/file_loader.rb create mode 100644 spec/acceptance/file_attributes_spec.rb create mode 100644 spec/factory_bot/file_loader_spec.rb create mode 100644 spec/support/text_file.txt diff --git a/lib/factory_bot.rb b/lib/factory_bot.rb index fdb2ba608..34a76b86c 100644 --- a/lib/factory_bot.rb +++ b/lib/factory_bot.rb @@ -46,6 +46,7 @@ require "factory_bot/linter" require "factory_bot/version" require "factory_bot/internal" +require "factory_bot/file_loader" module FactoryBot Deprecation = ActiveSupport::Deprecation.new("6.0", "factory_bot") diff --git a/lib/factory_bot/errors.rb b/lib/factory_bot/errors.rb index bcab0baea..5fe34e45e 100644 --- a/lib/factory_bot/errors.rb +++ b/lib/factory_bot/errors.rb @@ -22,4 +22,6 @@ class MethodDefinitionError < RuntimeError; end # Raised when any factory is considered invalid class InvalidFactoryError < RuntimeError; end + + class FileDoesNotExistError < RuntimeError; end end diff --git a/lib/factory_bot/file_loader.rb b/lib/factory_bot/file_loader.rb new file mode 100644 index 000000000..ee13cc386 --- /dev/null +++ b/lib/factory_bot/file_loader.rb @@ -0,0 +1,39 @@ +require "tempfile" + +module FactoryBot + class FileLoader + attr_reader :tempfile, :original_filename + + def initialize(file_path) + from_file_path(file_path) + end + + def path + tempfile.path + end + + def method_missing(method_name, *args, &block) # rubocop:disable Style/MethodMissing + tempfile.public_send(method_name, *args, &block) + end + + def respond_to_missing?(method_name, include_private = false) + tempfile.respond_to?(method_name, include_private) || super + end + + private + + def from_file_path(path) + raise FileDoesNotExistError unless ::File.exist?(path) + + @original_filename = ::File.basename(path) + extension = ::File.extname(@original_filename) + + @tempfile = Tempfile.new( + [::File.basename(@original_filename, extension), extension], + ) + @tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding) + + FileUtils.copy_file(path, @tempfile.path) + end + end +end diff --git a/lib/factory_bot/syntax/methods.rb b/lib/factory_bot/syntax/methods.rb index d03f63f28..542f97c41 100644 --- a/lib/factory_bot/syntax/methods.rb +++ b/lib/factory_bot/syntax/methods.rb @@ -129,6 +129,20 @@ def generate_list(name, count) Internal.sequence_by_name(name).next end end + + # Creates a copy of the file passed to attach to the attribute + # + # Arguments: + # filepath: (String) + # The path for the file that will be loaded. Should be a valid path + # for a file that exists at that location. + # + # Returns: + # An instance of the file loader class pointing to the file at the + # provided path. + def file_fixture(file_path) + FileLoader.new(file_path) + end end end end diff --git a/spec/acceptance/file_attributes_spec.rb b/spec/acceptance/file_attributes_spec.rb new file mode 100644 index 000000000..d10c23cd8 --- /dev/null +++ b/spec/acceptance/file_attributes_spec.rb @@ -0,0 +1,20 @@ +describe "file attributes" do + context "when an attribute uses a file" do + it "assigns an file to the attribute" do + define_class("Post") do |_class| + attr_accessor :attachment + end + filename = File.expand_path("spec/support/text_file.txt") + file_contents = File.read(filename) + + FactoryBot.define do + factory :post do + attachment { file_fixture(filename) } + end + end + post = FactoryBot.build(:post) + expect(post.attachment.original_filename).to eq ::File.basename filename + expect(File.read(post.attachment)).to eq file_contents + end + end +end diff --git a/spec/factory_bot/file_loader_spec.rb b/spec/factory_bot/file_loader_spec.rb new file mode 100644 index 000000000..ebc702801 --- /dev/null +++ b/spec/factory_bot/file_loader_spec.rb @@ -0,0 +1,21 @@ +describe FactoryBot::FileLoader do + it "creates a tempfile copy from the original" do + original_file_path = File.expand_path("spec/support/text_file.txt") + original_file_contents = File.read(original_file_path) + file_loader = FactoryBot::FileLoader.new(original_file_path) + + expect(original_file_contents).to eq File.read(file_loader.tempfile) + end + + it "delegates all the methods to the tempfile" do + original_file_path = File.expand_path("spec/support/text_file.txt") + file_loader = FactoryBot::FileLoader.new(original_file_path) + + expect(file_loader.path).to eq file_loader.tempfile.path + end + + it "raises an exception is the file path does not exists" do + expect { FactoryBot::FileLoader.new("nonexistent.path") }. + to raise_error FactoryBot::FileDoesNotExistError + end +end diff --git a/spec/support/text_file.txt b/spec/support/text_file.txt new file mode 100644 index 000000000..33def9af0 --- /dev/null +++ b/spec/support/text_file.txt @@ -0,0 +1 @@ +Test Content