Class: Template::Spec

Inherits:
Object
  • Object
show all
Defined in:
lib/template/spec.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeSpec

Returns a new instance of Spec.



53
54
55
56
57
58
59
60
# File 'lib/template/spec.rb', line 53

def initialize
  @tokens = Hash[]
  @required_files = Array.new

  self[:BLANK] = Template::Token.new('', true)
  self[:DATE] = Template::Token.new(DateTime.now.strftime('%F'), true)
  self[:YEAR] = Template::Token.new(DateTime.now.strftime('%Y'), true)
end

Instance Attribute Details

#block_actions_under_gitObject

Returns the value of attribute block_actions_under_git.



8
9
10
# File 'lib/template/spec.rb', line 8

def block_actions_under_git
  @block_actions_under_git
end

#identifierObject

Returns the value of attribute identifier.



8
9
10
# File 'lib/template/spec.rb', line 8

def identifier
  @identifier
end

#post_actionsObject

Returns the value of attribute post_actions.



9
10
11
# File 'lib/template/spec.rb', line 9

def post_actions
  @post_actions
end

#required_filesObject (readonly)

Returns the value of attribute required_files.



9
10
11
# File 'lib/template/spec.rb', line 9

def required_files
  @required_files
end

Class Method Details

.expand_identifier(template_identifier) ⇒ Object



41
42
43
44
45
# File 'lib/template/spec.rb', line 41

def self.expand_identifier(template_identifier)
  path = spec_path_for_identifier template_identifier
  hash = hash_for_spec_path path
  hash_to_identifier hash
end

.from_file(path) ⇒ Object



47
48
49
50
51
# File 'lib/template/spec.rb', line 47

def self.from_file(path)
  string = IO.read path
  spec = Template::Spec.new
  spec.from_string(string, path)
end

.hash_for_spec_path(spec_path) ⇒ Object



29
30
31
32
33
34
35
# File 'lib/template/spec.rb', line 29

def self.hash_for_spec_path(spec_path)
  relative_spec = spec_path.gsub(Klipp::Configuration.root_dir, '')
  {
      name: File.basename(spec_path, '.klippspec'),
      repo: relative_spec.split(File::SEPARATOR).map { |x| x=="" ? File::SEPARATOR : x }[1..-1].first
  }
end

.hash_to_identifier(template_hash) ⇒ Object



37
38
39
# File 'lib/template/spec.rb', line 37

def self.hash_to_identifier(template_hash)
  "#{template_hash[:repo]}/#{template_hash[:name]}"
end

.identifier_is_ambiguous(identifier) ⇒ Object



11
12
13
# File 'lib/template/spec.rb', line 11

def self.identifier_is_ambiguous(identifier)
  specs_matching_identifier(identifier).count == 1
end

.spec_path_for_identifier(identifier) ⇒ Object



22
23
24
25
26
27
# File 'lib/template/spec.rb', line 22

def self.spec_path_for_identifier(identifier)
  specs = specs_matching_identifier identifier
  raise "Unknown template: #{identifier}. Use `klipp template list` to see your options" unless specs && specs.count > 0
  raise "Found multiple templates named #{identifier}, use a full template identifier to pick one. Run `klipp template list` to see your options" if specs && specs.count > 1
  specs.first
end

.specs_matching_identifier(identifier) ⇒ Object



15
16
17
18
19
20
# File 'lib/template/spec.rb', line 15

def self.specs_matching_identifier(identifier)
  chunks = identifier.split(File::SEPARATOR)
  name = chunks.pop
  repo = chunks.count > 0 ? File.join(chunks.pop, '**') : '**'
  Dir.glob(File.join(Klipp::Configuration.root_dir, repo, "#{name}.klippspec"))
end

Instance Method Details

#[](name) ⇒ Object



113
114
115
# File 'lib/template/spec.rb', line 113

def [](name)
  @tokens[name]
end

#[]=(name, token) ⇒ Object



117
118
119
120
# File 'lib/template/spec.rb', line 117

def []=(name, token)
  raise "Redeclaring tokens not allowed: #{name}" if @tokens[name]
  @tokens[name] = token
end

#confirm_required_filesObject



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/template/spec.rb', line 158

def confirm_required_files()
  missing_files = Array.new
  self.required_files.each do |required_file|
    file_path = self.replace_tokens File.join(required_file.directory, required_file.name)
    FileUtils.mkdir_p File.dirname(file_path)
    missing_files << required_file unless File.exists? file_path
  end

  if missing_files.length > 0
    message = "Required file#{missing_files.length > 1 ? 's' : ''} not found:\n\n"
    missing_files.each do |missing_file|
      file_path = self.replace_tokens File.join(missing_file.directory, missing_file.name)
      message << "\t#{file_path} - #{missing_file.comment ? "#{missing_file.comment}" : ''}\n"
    end
    raise message
  end
end

#eachObject



180
181
182
# File 'lib/template/spec.rb', line 180

def each
  @tokens.each { |name, token| yield(name, token) }
end

#from_string(string, path) ⇒ Object



86
87
88
89
90
91
92
93
# File 'lib/template/spec.rb', line 86

def from_string(string, path)
  begin
    eval(string, nil, path)
  rescue Exception => e
    raise "Error evaluating spec: #{File.basename(path)}: #{e.message}\n  #{e.backtrace.join("\n  ")}"
  end
  validate_spec
end

#invalidate(message) ⇒ Object



176
177
178
# File 'lib/template/spec.rb', line 176

def invalidate(message)
  raise message
end

#klippfileObject



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/template/spec.rb', line 191

def klippfile
  kf = "create '#{self.class.expand_identifier(self.identifier)}' do |tokens|\n\n"
  @tokens.each do |name, token|
    unless token.hidden
      kf += "  # #{token.comment}\n" if token.comment
      kf += "  # #{token.validation_hint}\n" if token.validation_hint
      if token.type == :bool
        kf += "  tokens[:#{name}] = #{token.default_value ? 'true' : 'false'}\n"
      else
        kf += "  tokens[:#{name}] = \"#{token.default_value}\"\n"
      end
      kf += "\n"
    end
  end
  kf += "end"
end

#klippspecObject



208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/template/spec.rb', line 208

def klippspec
  ks = "# encoding=utf-8\n"
  ks += "\n"
  ks += "spec '#{identifier}' do |s|\n"
  ks += "  s.block_actions_under_git = true\n"
  ks += "  # s.pre_actions = ['echo \"Hello klipp!\"']\n"
  ks += "  # s.post_actions = ['pod install']\n"
  ks += "\n"
  ks += "  s.token :REPLACEABLE do |t|\n"
  ks += "    t.comment = \"Replaceable value (to insert in any filename or string containing 'XXREPLACEABLEXX')\"\n"
  ks += "    t.validation = /^[A-Z][A-Za-z0-9 ]{2,}$/\n"
  ks += "    t.validation_hint = 'At least three characters long, start with a capital character, may contain spaces'\n"
  ks += "  end\n"
  ks += "\n"
  ks += "  s.token :TOGGLE do |t|\n"
  ks += "    t.comment = \"Toggle value (to insert in any filename or string containing 'XXTOGGLEXX')\"\n"
  ks += "    t.type = :bool\n"
  ks += "    # t.bool_strings = ['NO','YES']\n"
  ks += "  end\n"
  ks += "\n"
  ks += "  # ...\n"
  ks += "\n"
  ks += "end\n"
end

#post_action=(action) ⇒ Object



62
63
64
# File 'lib/template/spec.rb', line 62

def post_action=(action)
  post_actions << action
end

#pre_action=(action) ⇒ Object



74
75
76
# File 'lib/template/spec.rb', line 74

def pre_action=(action)
  pre_actions << action
end

#pre_actionsObject



78
79
80
# File 'lib/template/spec.rb', line 78

def pre_actions
  @pre_actions ||= []
end

#pre_actions=(pre_actions) ⇒ Object



82
83
84
# File 'lib/template/spec.rb', line 82

def pre_actions=(pre_actions)
  @pre_actions = pre_actions.is_a?(Array) ? pre_actions : [pre_actions.to_s]
end

#replace_tokens(string_with_tokens, delimiter = 'XX') ⇒ Object



262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/template/spec.rb', line 262

def replace_tokens(string_with_tokens, delimiter='XX')
  unless string_with_tokens.valid_encoding?
    raise "Invalid string encoding #{string_with_tokens.encoding}"
  end

  replaced = string_with_tokens
  @tokens.each do |name, token|
    needle = delimiter+name.to_s+delimiter
    replacement = token.to_s
    replaced.gsub!(needle, replacement)
  end
  replaced
end

#required_file(name, &config) ⇒ Object



184
185
186
187
188
189
# File 'lib/template/spec.rb', line 184

def required_file name, &config
  required_file = Template::RequiredFile.new name
  raise 'Incomplete file configuration' unless block_given?
  config.yield(required_file)
  @required_files << required_file
end

#set_token_values(tokens, verbose = false) ⇒ Object



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
154
155
156
# File 'lib/template/spec.rb', line 128

def set_token_values(tokens, verbose=false)
  token_errors = Hash[]
  puts() if verbose
  tokens.each do |name, value|
    token = self[name]
    begin
      if token
        Formatador.display_line("#{name}: [bold]#{value}[/]") if verbose
        token.value = value
      else
        token_errors[name] = "unanticipated token encountered in the Klippfile :#{name}"
      end
    rescue Exception => e
      token_errors[name] = "token :#{name}. #{e.message}"
    end
  end

  @tokens.each do |name, token|
    token_errors[name] = "missing value for token :#{name}" if token.value == nil && token_errors[name] == nil
  end

  if token_errors.length > 0
    msg = "Token configuration error:\n\n\t"
    msg << token_errors.map { |name, error_message| error_message }.join("\n\t")
    invalidate msg
  end

  puts() if verbose
end

#spec(identifier, &config) ⇒ Object

This method is called from the klippspec



96
97
98
99
100
101
102
103
104
# File 'lib/template/spec.rb', line 96

def spec identifier, &config
  self.identifier = identifier
  begin
    config.yield(self) if (block_given?)
  rescue Exception => e
    raise "Invalid klippspec configuration: #{e.message}\n  #{e.backtrace.join("\n  ")}"
  end
  validate_spec
end

#target_file(source_dir, source_file, target_dir) ⇒ Object



233
234
235
236
237
# File 'lib/template/spec.rb', line 233

def target_file(source_dir, source_file, target_dir)
  stripped_path = source_file.gsub(source_dir, '')
  customizable_path = replace_tokens(stripped_path)
  File.join(target_dir, customizable_path)
end

#token(identifier, &config) ⇒ Object



106
107
108
109
110
111
# File 'lib/template/spec.rb', line 106

def token identifier, &config
  token = Template::Token.new
  raise 'Incomplete token configuration' unless block_given?
  config.yield(token)
  self[identifier] = token
end

#transfer_file(source_file, target_file, overwrite) ⇒ Object



239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/template/spec.rb', line 239

def transfer_file(source_file, target_file, overwrite)
  FileUtils.mkdir_p File.dirname(target_file)

  if File.directory? source_file
    FileUtils.mkdir_p target_file
  elsif !File.exists?(target_file) || overwrite
    if File.binary? source_file
      FileUtils.cp(source_file, target_file)
    else
      FileUtils.cp(source_file, target_file)
      begin
        IO.write target_file, replace_tokens(File.read(source_file))
      rescue
        # nothing
      end
    end
  else
    raise "#{target_file} already exists, not overwriting. Use -f to force overwriting."
  end

  target_file
end

#validate_specObject



122
123
124
125
126
# File 'lib/template/spec.rb', line 122

def validate_spec
  msg = 'Template configuration invalid: '
  invalidate msg+'missing name' unless @identifier && @identifier.length > 0
  self
end