diff --git a/Gemfile b/Gemfile index a5b7d401f48..aec73f91827 100644 --- a/Gemfile +++ b/Gemfile @@ -125,16 +125,21 @@ group :development do gem "better_errors" gem "binding_of_caller" gem "debug_inspector" + gem "faker" gem "listen" + gem "timecop" gem "vendorer" end +group :development, :test do + gem "factory_bot_rails" +end + # Gems needed for running tests group :test do gem "brakeman" gem "capybara", ">= 2.15" gem "erb_lint", :require => false - gem "factory_bot_rails" gem "minitest", "~> 5.1" gem "puma", "~> 5.0" gem "rails-controller-testing" diff --git a/Gemfile.lock b/Gemfile.lock index 31d64b6b8c7..eb923944d76 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -197,6 +197,8 @@ GEM factory_bot_rails (6.1.0) factory_bot (~> 6.1.0) railties (>= 5.0.0) + faker (2.16.0) + i18n (>= 1.6, < 2) faraday (1.4.1) faraday-excon (~> 1.1) faraday-net_http (~> 1.0) @@ -448,6 +450,7 @@ GEM thor (1.1.0) thread_safe (0.3.6) tilt (2.0.10) + timecop (0.9.4) tzinfo (1.2.9) thread_safe (~> 0.1) uglifier (4.2.0) @@ -496,6 +499,7 @@ DEPENDENCIES delayed_job_active_record erb_lint factory_bot_rails + faker faraday ffi-libarchive gd2-ffij (>= 0.4.0) @@ -547,6 +551,7 @@ DEPENDENCIES simplecov simplecov-lcov strong_migrations + timecop uglifier (>= 1.3.0) validates_email_format_of (>= 1.5.1) vendorer diff --git a/config/initializers/factory_bot_strategy_find_or_create.rb b/config/initializers/factory_bot_strategy_find_or_create.rb new file mode 100644 index 00000000000..0e9e7cdf092 --- /dev/null +++ b/config/initializers/factory_bot_strategy_find_or_create.rb @@ -0,0 +1,33 @@ +# From https://stackoverflow.com/a/65700370/ +if Rails.env.development? + module FactoryBot + module Strategy + # Does not work when passing objects as associations: `FactoryBot.find_or_create(:entity, association: object)` + # Instead do: `FactoryBot.find_or_create(:entity, association_id: id)` + class FindOrCreate + def initialize + @build_strategy = FactoryBot.strategy_by_name(:build).new + end + + delegate :association, :to => :@build_strategy + + def result(evaluation) + attributes = attributes_shared_with_build_result(evaluation) + evaluation.object.class.where(attributes).first || FactoryBot.strategy_by_name(:create).new.result(evaluation) + end + + private + + # Here we handle possible mismatches between initially provided attributes and actual model attrbiutes + # For example, devise's User model is given a `password` and generates an `encrypted_password` + # In this case, we shouldn't use `password` in the `where` clause + def attributes_shared_with_build_result(evaluation) + object_attributes = evaluation.object.attributes + evaluation.hash.filter { |k, _v| object_attributes.key?(k.to_s) } + end + end + end + + register_strategy(:find_or_create, Strategy::FindOrCreate) + end +end diff --git a/lib/tasks/development.rake b/lib/tasks/development.rake new file mode 100644 index 00000000000..152599d335f --- /dev/null +++ b/lib/tasks/development.rake @@ -0,0 +1,28 @@ +# Create some fake data to help developers get started with the app + +# This rake task will add some fake data, which is particularly helpful when +# developing the user interface, since it avoids time-consuming manual work + +# The data should be created in a idempotent fashion, so that the script can be +# run again without causing errors or duplication. + +if Rails.env.development? + desc "Populate the development database with some fake data" + namespace "dev" do + task "populate" => :environment do + include FactoryBot::Syntax::Methods + + # Ensure we generate the same data each time this is run + Faker::Config.random = Random.new(42) + + # Ensure that all dates (e.g. terms_agreed) are consistent + Timecop.freeze(Time.utc(2015, 10, 21, 12, 0, 0)) do + user = find_or_create(:user, :display_name => Faker::Name.name) + + find_or_create(:diary_entry, :title => Faker::Lorem.sentence, + :body => Array.new(3) { Faker::Lorem.paragraph(:sentence_count => 12) }.join("\n\n"), + :user_id => user.id) + end + end + end +end