Skip to content

Commit cc79ec2

Browse files
committed
Add bootcamp syncer
1 parent c2805f1 commit cc79ec2

File tree

14 files changed

+305
-72
lines changed

14 files changed

+305
-72
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
class Git::SyncBootcampContent
2+
include Mandate
3+
4+
queue_as :default
5+
6+
def call
7+
repo.update!
8+
9+
repo.levels.each do |data|
10+
level = Bootcamp::Level.find_or_create_by!(idx: data.idx) do |l|
11+
l.title = data.title
12+
l.description = data.description
13+
l.content_markdown = data.content
14+
end
15+
level.update(
16+
title: data.title,
17+
description: data.description,
18+
content_markdown: data.content
19+
)
20+
rescue StandardError => e
21+
raise if Rails.env.development?
22+
23+
Bugsnag.notify(e)
24+
end
25+
26+
repo.concepts.each do |data|
27+
concept = Bootcamp::Concept.find_or_create_by!(uuid: data.uuid) do |c|
28+
c.slug = data.slug
29+
c.title = data.title
30+
c.description = data.description
31+
c.level_idx = data.level
32+
c.apex = data.apex
33+
end
34+
concept.update(
35+
slug: data.slug,
36+
title: data.title,
37+
description: data.description,
38+
level_idx: data.level,
39+
apex: data.apex
40+
)
41+
rescue StandardError => e
42+
raise if Rails.env.development?
43+
44+
Bugsnag.notify(e)
45+
end
46+
end
47+
48+
private
49+
delegate :head_commit, to: :repo
50+
51+
memoize
52+
def repo = Git::BootcampContent.new
53+
end

app/models/git/bootcamp_content.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
class Git::BootcampContent
2+
extend Mandate::Memoize
3+
4+
DEFAULT_REPO_URL = "[email protected]:exercism/bootcamp-content.git".freeze
5+
6+
attr_reader :repo
7+
8+
def self.update! = new.update!
9+
10+
def initialize(repo_url: DEFAULT_REPO_URL, repo: nil)
11+
@repo = repo || Git::Repository.new(repo_url:)
12+
end
13+
14+
def update! = repo.fetch!
15+
16+
memoize
17+
def levels
18+
Git::BootcampContent::Levels.new(repo:)
19+
end
20+
21+
memoize
22+
def projects
23+
project_dir_entries.map do |entry|
24+
Git::BootcampContent::Project.new(entry[:name], repo:)
25+
end
26+
end
27+
28+
memoize
29+
def concepts
30+
Git::BootcampContent::Concepts.new(repo:)
31+
end
32+
33+
private
34+
delegate :head_commit, to: :repo
35+
36+
memoize
37+
def project_dir_entries
38+
repo.fetch_tree(repo.head_commit, "projects").select { |entry| entry[:type] == :tree }
39+
end
40+
end
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
class Git::BootcampContent::Concept
2+
extend Mandate::Memoize
3+
extend Mandate::InitializerInjector
4+
extend Git::HasGitFilepath
5+
6+
delegate :head_sha, :lookup_commit, :head_commit, to: :repo
7+
8+
attr_reader :config
9+
10+
def initialize(config, repo_url: Git::BootcampContent::DEFAULT_REPO_URL, repo: nil)
11+
@repo = repo || Git::Repository.new(repo_url:)
12+
@config = config
13+
end
14+
15+
memoize
16+
def uuid = config[:uuid]
17+
def slug = config[:slug]
18+
def title = config[:title]
19+
def description = config[:description]
20+
def level = config[:level]
21+
def apex = !!config[:apex]
22+
def content = read_file_blob("#{slug}.md")
23+
24+
private
25+
attr_reader :repo
26+
27+
def absolute_filepath(filepath) = "#{dir}/#{filepath}"
28+
29+
memoize
30+
def commit = repo.lookup_commit(repo.head_sha)
31+
32+
memoize
33+
def absolute_filepaths = filepaths.map { |filepath| absolute_filepath(filepath) }
34+
def filepaths = file_entries.map { |defn| defn[:full] }
35+
def dir = "concepts"
36+
37+
def read_file_blob(filepath)
38+
mapped = file_entries.map { |f| [f[:full], f[:oid]] }.to_h
39+
mapped[filepath] ? repo.read_blob(mapped[filepath]) : nil
40+
end
41+
end
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
class Git::BootcampContent::Concepts
2+
extend Mandate::Memoize
3+
extend Mandate::InitializerInjector
4+
extend Git::HasGitFilepath
5+
6+
delegate :head_sha, :lookup_commit, :head_commit, to: :repo
7+
8+
git_filepath :config, file: "config.json"
9+
10+
def initialize(repo_url: Git::BootcampContent::DEFAULT_REPO_URL, repo: nil)
11+
@repo = repo || Git::Repository.new(repo_url:)
12+
end
13+
14+
memoize
15+
def concepts
16+
concept_documents.map do |entry|
17+
concept_config = config.find { |config| config[:slug] == File.basename(entry[:name], ".*") }
18+
Git::BootcampContent::Concept.new(
19+
concept_config,
20+
repo:
21+
)
22+
end
23+
end
24+
25+
def each(&block) = concepts.each(&block)
26+
27+
private
28+
delegate :head_commit, to: :repo
29+
attr_reader :repo
30+
31+
memoize
32+
def concept_documents
33+
repo.fetch_tree(repo.head_commit, dir).select { |entry| entry[:type] == :blob && File.extname(entry[:name]) == ".md" }
34+
end
35+
36+
def absolute_filepath(filepath) = "#{dir}/#{filepath}"
37+
38+
memoize
39+
def commit = repo.lookup_commit(repo.head_sha)
40+
41+
memoize
42+
def absolute_filepaths = filepaths.map { |filepath| absolute_filepath(filepath) }
43+
def filepaths = file_entries.map { |defn| defn[:full] }
44+
def dir = "concepts"
45+
end
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
class Git::BootcampContent::Level
2+
extend Mandate::Memoize
3+
extend Mandate::InitializerInjector
4+
extend Git::HasGitFilepath
5+
6+
delegate :head_sha, :lookup_commit, :head_commit, to: :repo
7+
8+
git_filepath :content, file: "content.md"
9+
10+
attr_reader :idx, :config
11+
12+
def initialize(idx, config, repo_url: Git::BootcampContent::DEFAULT_REPO_URL, repo: nil)
13+
@repo = repo || Git::Repository.new(repo_url:)
14+
@idx = idx
15+
@config = config
16+
end
17+
18+
memoize
19+
def title = config[:title]
20+
def description = config[:description]
21+
22+
private
23+
attr_reader :repo
24+
25+
def absolute_filepath(filepath) = "#{dir}/#{filepath}"
26+
27+
memoize
28+
def commit = repo.lookup_commit(repo.head_sha)
29+
30+
memoize
31+
def absolute_filepaths = filepaths.map { |filepath| absolute_filepath(filepath) }
32+
def filepaths = file_entries.map { |defn| defn[:full] }
33+
def dir = "levels/#{idx}"
34+
end
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
class Git::BootcampContent::Levels
2+
extend Mandate::Memoize
3+
extend Mandate::InitializerInjector
4+
extend Git::HasGitFilepath
5+
6+
delegate :head_sha, :lookup_commit, :head_commit, to: :repo
7+
8+
git_filepath :config, file: "config.json"
9+
10+
def initialize(repo_url: Git::BootcampContent::DEFAULT_REPO_URL, repo: nil)
11+
@repo = repo || Git::Repository.new(repo_url:)
12+
end
13+
14+
memoize
15+
def levels
16+
level_dir_entries.map do |entry|
17+
level_config = config.find { |config| config[:idx] == entry[:name].to_i }
18+
Git::BootcampContent::Level.new(
19+
entry[:name],
20+
level_config,
21+
repo:
22+
)
23+
end
24+
end
25+
26+
def each(&block) = levels.each(&block)
27+
28+
private
29+
delegate :head_commit, to: :repo
30+
attr_reader :repo
31+
32+
memoize
33+
def level_dir_entries
34+
repo.fetch_tree(repo.head_commit, "levels").select { |entry| entry[:type] == :tree }
35+
end
36+
37+
def absolute_filepath(filepath) = "#{dir}/#{filepath}"
38+
39+
memoize
40+
def commit = repo.lookup_commit(repo.head_sha)
41+
42+
memoize
43+
def absolute_filepaths = filepaths.map { |filepath| absolute_filepath(filepath) }
44+
def filepaths = file_entries.map { |defn| defn[:full] }
45+
def dir = "levels"
46+
end

app/models/git/problem_specifications/exercise.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def file_entries
6262
tree.walk(:preorder).map do |root, entry|
6363
next if entry[:type] == :tree
6464

65-
entry[:full] = "#{root}#{entry[:name]}"
65+
entry[:full] = "#{root}#yentry[:name]}"
6666
entry
6767
end.compact
6868
rescue Rugged::TreeError

bootcamp_content/levels/1.md

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

bootcamp_content/levels/2.md

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

bootcamp_content/levels/3.md

Whitespace-only changes.

bootcamp_content/levels/4.md

Whitespace-only changes.

bootcamp_content/levels/config.json

Lines changed: 0 additions & 18 deletions
This file was deleted.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class AddUuidsToBootcamp < ActiveRecord::Migration[7.0]
2+
def change
3+
return if Rails.env.production?
4+
5+
add_column :bootcamp_concepts, :uuid, :string, null: true
6+
add_column :bootcamp_projects, :uuid, :string, null: true
7+
add_column :bootcamp_exercises, :uuid, :string, null: true
8+
9+
# change_column_null :bootcamp_concepts, :uuid, false
10+
# change_column_null :bootcamp_projects, :uuid, false
11+
# change_column_null :bootcamp_exercises, :uuid, false
12+
13+
end
14+
end

0 commit comments

Comments
 (0)