Skip to content

Commit a00ca85

Browse files
authored
Fix spec for projects:create_starter Rake task (#266)
The `spec/tasks/create_starter_spec.rb` was triggering a `NameError` exception when run on its own (using `bundle exec rspec spec/tasks/create_starter_spec.rb`) and when the whole test suite was run in a certain order (e.g. `bundle exec rspec --seed 54367`). The main goal of this PR is to fix that problem. I've achieved that by extracting all the code out of the Rake task and into a new `FilesystemProject.import_all!` method and testing that directly rather than testing the Rake task which tends to cause class loading problems. As part of doing this, I've also removed the stubbing from the spec, because I found it confusing and it turns out it doesn't take too long to run import all the projects for real in the spec. See individual commit notes for more details.
2 parents 73ef8a7 + 6831a1d commit a00ca85

File tree

9 files changed

+74
-70
lines changed

9 files changed

+74
-70
lines changed

.rubocop_todo.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@
99
# Offense count: 2
1010
Lint/UselessAssignment:
1111
Exclude:
12-
- 'lib/tasks/projects.rake'
12+
- 'app/models/filesystem_project.rb'

Procfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
web: bundle exec puma -C config/puma.rb
2-
release: bundle exec rails db:migrate && bundle exec rake projects:create_starter
2+
release: bundle exec rails db:migrate && bundle exec rake projects:create_all
33
worker: bundle exec good_job start --max-threads=8

app/models/filesystem_project.rb

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# frozen_string_literal: true
2+
3+
require 'yaml'
4+
5+
class FilesystemProject
6+
CODE_FORMATS = ['.py', '.csv', '.txt', '.html', '.css'].freeze
7+
IMAGE_FORMATS = ['.png', '.jpg', '.jpeg', '.webp'].freeze
8+
PROJECTS_ROOT = Rails.root.join('lib/tasks/project_components')
9+
10+
def self.import_all! # rubocop:disable Metrics/AbcSize
11+
PROJECTS_ROOT.each_child do |dir|
12+
proj_config = YAML.safe_load_file(dir.join('project_config.yml').to_s)
13+
files = dir.children
14+
code_files = files.filter { |file| CODE_FORMATS.include? File.extname(file) }
15+
image_files = files.filter { |file| IMAGE_FORMATS.include? File.extname(file) }
16+
17+
components = []
18+
code_files.each do |file|
19+
components << component(file, dir)
20+
end
21+
22+
images = []
23+
image_files.each do |file|
24+
images << image(file, dir)
25+
end
26+
27+
project_importer = ProjectImporter.new(name: proj_config['NAME'], identifier: proj_config['IDENTIFIER'],
28+
type: proj_config['TYPE'] || 'python',
29+
locale: proj_config['LOCALE'] || 'en', components:, images:)
30+
project_importer.import!
31+
end
32+
end
33+
34+
def self.component(file, dir)
35+
name = File.basename(file, '.*')
36+
extension = File.extname(file).delete('.')
37+
code = File.read(dir.join(File.basename(file)).to_s)
38+
default = (File.basename(file) == 'main.py')
39+
component = { name:, extension:, content: code, default: }
40+
end
41+
42+
def self.image(file, dir)
43+
filename = File.basename(file)
44+
io = File.open(dir.join(filename).to_s)
45+
image = { filename:, io: }
46+
end
47+
end

db/seeds.rb

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,4 @@
77
#
88
# movies = Movie.create([{ name: "Star Wars" }, { name: "Lord of the Rings" }])
99
# Character.create(name: "Luke", movie: movies.first)
10-
words = File.readlines('words.txt').map { |w| w.delete!("\n") }
11-
12-
words.each do |word|
13-
Word.create(word: word)
14-
end
10+
PhraseIdentifier.seed!

lib/phrase_identifier.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ class PhraseIdentifier
44
class Error < RuntimeError
55
end
66

7+
def self.seed!
8+
words = File.readlines('words.txt', chomp: true)
9+
Word.delete_all
10+
words.each { |word| Word.create!(word:) }
11+
end
12+
713
def self.generate
814
10.times do
915
phrase = Word.order(Arel.sql('RANDOM()')).take(3).pluck(:word).join('-')

lib/tasks/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Please note that in this document, to avoid confusion, `project` refers to an en
99
## Creating the project directory
1010
Each version of a Projects site 'project' such as a starter or finished example needs its own directory in `/project_components` as they will form separate `project`s in the database. Although the naming of these directories is inconsequential, at the moment they are roughly named in the form `{project_name}_starter` and `{project_name}_example` for the sake of consistency.
1111

12-
Each directory for a `project` should contain copies of the `python` files and any `text`, `csv` and image files that the `project` should contain. Any content reused across multiple `project`s should be duplicated in the relevant directory for each `project`.
12+
Each directory for a `project` should contain copies of the `python` files and any `text`, `csv` and image files that the `project` should contain. Any content reused across multiple `project`s should be duplicated in the relevant directory for each `project`.
1313

1414
### Populating `project_config.yml`
1515
Every directory representing a `project` must contain a `project_config.yml`. This should include the following information:
@@ -27,7 +27,7 @@ Every directory representing a `project` must contain a `project_config.yml`. Th
2727
An example `project_config.yml` with all of the above properties can be seen [here](https://github.com/RaspberryPiFoundation/editor-api/blob/main/lib/tasks/project_components/persuasive_data_presentation_iss_starter/project_config.yml).
2828

2929
## Getting the projects created in the database
30-
Please commit the required changes to a branch in the [`editor-api` repository](https://github.com/RaspberryPiFoundation/editor-ui/) and create a pull request to merge your branch into `main`. Once merged, we will run the task to create your `project`s in the database.
30+
Please commit the required changes to a branch in the [`editor-api` repository](https://github.com/RaspberryPiFoundation/editor-api/) and create a pull request to merge your branch into `main`. Once merged, we will run the task to create your `project`s in the database.
3131

3232
## Amending existing projects
3333
Existing `project`s can be ammended by updating the content in the directory corresponding to that `project`. Please create a pull request with the required changes as described above and we will ensure they are applied once the pull request has been merged.

lib/tasks/projects.rake

Lines changed: 3 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,8 @@
11
# frozen_string_literal: true
22

3-
require 'yaml'
4-
5-
CODE_FORMATS = ['.py', '.csv', '.txt', '.html', '.css'].freeze
6-
IMAGE_FORMATS = ['.png', '.jpg', '.jpeg', '.webp'].freeze
7-
83
namespace :projects do
9-
desc 'Import starter projects'
10-
task create_starter: :environment do
11-
Dir.each_child("#{File.dirname(__FILE__)}/project_components") do |dir|
12-
proj_config = YAML.safe_load(File.read("#{File.dirname(__FILE__)}/project_components/#{dir}/project_config.yml"))
13-
files = Dir.children("#{File.dirname(__FILE__)}/project_components/#{dir}")
14-
code_files = files.filter { |file| CODE_FORMATS.include? File.extname(file) }
15-
image_files = files.filter { |file| IMAGE_FORMATS.include? File.extname(file) }
16-
17-
components = []
18-
code_files.each do |file|
19-
components << component(file, dir)
20-
end
21-
22-
images = []
23-
image_files.each do |file|
24-
images << image(file, dir)
25-
end
26-
27-
project_importer = ProjectImporter.new(name: proj_config['NAME'], identifier: proj_config['IDENTIFIER'],
28-
type: proj_config['TYPE'] ||= 'python',
29-
locale: proj_config['LOCALE'] ||= 'en', components:, images:)
30-
project_importer.import!
31-
end
4+
desc 'Import starter & example projects'
5+
task create_all: :environment do
6+
FilesystemProject.import_all!
327
end
338
end
34-
35-
private
36-
37-
def component(file, dir)
38-
name = File.basename(file, '.*')
39-
extension = File.extname(file).delete('.')
40-
code = File.read(File.dirname(__FILE__) + "/project_components/#{dir}/#{File.basename(file)}")
41-
default = (File.basename(file) == 'main.py')
42-
component = { name:, extension:, content: code, default: }
43-
end
44-
45-
def image(file, dir)
46-
filename = File.basename(file)
47-
io = File.open(File.dirname(__FILE__) + "/project_components/#{dir}/#{filename}")
48-
image = { filename:, io: }
49-
end
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# frozen_string_literal: true
2+
3+
require 'rails_helper'
4+
5+
describe FilesystemProject do
6+
before do
7+
PhraseIdentifier.seed!
8+
end
9+
10+
it 'imports all starter projects' do
11+
expect { described_class.import_all! }.not_to raise_error
12+
end
13+
end

spec/tasks/create_starter_spec.rb

Lines changed: 0 additions & 17 deletions
This file was deleted.

0 commit comments

Comments
 (0)