Class: SpaceArchitect::SpaceStore

Inherits:
Object
  • Object
show all
Defined in:
lib/space_architect/space_store.rb

Constant Summary collapse

MAX_CONCURRENT_CLONES =
5

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config:, state:, now: -> { Time.now }) ⇒ SpaceStore

Returns a new instance of SpaceStore.



20
21
22
23
24
# File 'lib/space_architect/space_store.rb', line 20

def initialize(config:, state:, now: -> { Time.now })
  @config = config
  @state = state
  @now = now
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



18
19
20
# File 'lib/space_architect/space_store.rb', line 18

def config
  @config
end

#nowObject (readonly)

Returns the value of attribute now.



18
19
20
# File 'lib/space_architect/space_store.rb', line 18

def now
  @now
end

#stateObject (readonly)

Returns the value of attribute state.



18
19
20
# File 'lib/space_architect/space_store.rb', line 18

def state
  @state
end

Instance Method Details

#add_repo(spec, from: Dir.pwd, scm: Pristine::SCM::Git.new, cloner: nil, mise_client: MiseClient.new) ⇒ Object



115
116
117
# File 'lib/space_architect/space_store.rb', line 115

def add_repo(spec, from: Dir.pwd, scm: Pristine::SCM::Git.new, cloner: nil, mise_client: MiseClient.new)
  add_repos([spec], from:, scm:, cloner:, mise_client:).fmap(&:first)
end

#add_repos(specs, from: Dir.pwd, scm: Pristine::SCM::Git.new, cloner: nil, mise_client: MiseClient.new, reporter: nil) ⇒ Object



119
120
121
# File 'lib/space_architect/space_store.rb', line 119

def add_repos(specs, from: Dir.pwd, scm: Pristine::SCM::Git.new, cloner: nil, mise_client: MiseClient.new, reporter: nil)
  current(from:).bind { |space| add_repos_to(space, specs, scm:, cloner:, mise_client:, reporter:) }
end

#add_repos_to(space, specs, scm: Pristine::SCM::Git.new, cloner: nil, mise_client: MiseClient.new, reporter: nil) ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/space_architect/space_store.rb', line 123

def add_repos_to(space, specs, scm: Pristine::SCM::Git.new, cloner: nil, mise_client: MiseClient.new, reporter: nil)
  additions = prepare_repo_additions(space, specs)
  first_error = nil

  Async do |task|
    semaphore = Async::Semaphore.new(MAX_CONCURRENT_CLONES, parent: task)

    clone_tasks = additions.map do |addition|
      semaphore.async(finished: false) do
        clone_addition(addition, scm:, cloner:, mise_client:, reporter:)
      end
    end

    # Collect results without raising inside the reactor so the outer task
    # succeeds and async does not log "Task may have ended" for our errors.
    clone_tasks.each do |ct|
      ct.wait
    rescue StandardError => e
      first_error ||= e
    end
  end.wait

  return Failure(first_error) if first_error

  Success(additions.map do |addition|
    repo_data = space.add_repo(addition.fetch(:reference), relative_path: addition.fetch(:relative_path), now: now.call)
    { space: space, repo: repo_data, reference: addition.fetch(:reference), path: addition.fetch(:path) }
  end)
rescue SpaceArchitect::Error => e
  Failure(e)
end

#create(title, git: true, git_client: GitClient.new) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/space_architect/space_store.rb', line 30

def create(title, git: true, git_client: GitClient.new)
  FileUtils.mkdir_p(spaces_dir)
  timestamp = now.call
  id = unique_id("#{timestamp.strftime('%Y%m%d')}-#{Slugger.slug(title)}")
  path = spaces_dir.join(id)

  FileUtils.mkdir_p(path.join("repos"))
  FileUtils.mkdir_p(path.join("notes"))
  FileUtils.mkdir_p(path.join("architecture"))
  FileUtils.mkdir_p(path.join("tmp"))
  FileUtils.mkdir_p(path.join("build"))
  File.write(path.join("build", ".keep"), "")

  space = Space.new(path, (id:, title:, timestamp:))
  space.save
  write_readme(path:, title:, id:, timestamp:)
  init_git(path:, id:, git_client:) if git
  state.touch_recent(id)
  Success(space)
rescue SpaceArchitect::Error => e
  Failure(e)
end

#current(from: Dir.pwd) ⇒ Object



87
88
89
90
91
# File 'lib/space_architect/space_store.rb', line 87

def current(from: Dir.pwd)
  current_from_pwd(from:).to_result(CurrentSpaceMissingError.new("No current space found from #{from}. Run this inside a space or pass a space id."))
rescue SpaceArchitect::Error => e
  Failure(e)
end

#current_from_pwd(from: Dir.pwd) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/space_architect/space_store.rb', line 93

def current_from_pwd(from: Dir.pwd)
  path = Pathname.new(File.expand_path(from.to_s))
  path = path.dirname if path.file?

  loop do
    return Some(Space.load(path)) if path.join(Space::METADATA_FILE).exist?
    break if path.root?

    path = path.parent
  end

  None()
end

#find(identifier = nil, from: Dir.pwd) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/space_architect/space_store.rb', line 63

def find(identifier = nil, from: Dir.pwd)
  value = identifier.to_s.strip
  return current(from:) if value.empty?

  if looks_like_path?(value)
    begin
      return Success(Space.load(File.expand_path(value)))
    rescue SpaceArchitect::Error => e
      return Failure(e)
    end
  end

  matches = matching_spaces(value)
  return Success(matches.first) if matches.length == 1

  if matches.empty?
    return Failure(NotFoundError.new("Could not find space matching '#{value}' in #{spaces_dir}"))
  end

  Failure(AmbiguousSpaceError.new("Space '#{value}' is ambiguous: #{matches.map(&:id).join(', ')}"))
rescue SpaceArchitect::Error => e
  Failure(e)
end

#listObject



53
54
55
56
57
58
59
60
61
# File 'lib/space_architect/space_store.rb', line 53

def list
  return [] unless spaces_dir.directory?

  spaces_dir.children.select(&:directory?).filter_map do |child|
    Space.load(child)
  rescue NotFoundError, Error
    nil
  end.sort_by(&:id)
end

#path_for(identifier = nil) ⇒ Object



107
108
109
# File 'lib/space_architect/space_store.rb', line 107

def path_for(identifier = nil)
  find(identifier).fmap(&:path)
end

#repos(from: Dir.pwd) ⇒ Object



155
156
157
# File 'lib/space_architect/space_store.rb', line 155

def repos(from: Dir.pwd)
  current(from:).fmap(&:repos)
end

#spaces_dirObject



26
27
28
# File 'lib/space_architect/space_store.rb', line 26

def spaces_dir
  config.spaces_dir
end

#use(identifier) ⇒ Object



111
112
113
# File 'lib/space_architect/space_store.rb', line 111

def use(identifier)
  find(identifier).fmap { |space| state.touch_recent(space.id); space }
end