Class: GitDis

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

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(repo_dir, redis_options = {}) ⇒ GitDis

Returns a new instance of GitDis.



92
93
94
95
96
97
# File 'lib/gitdis.rb', line 92

def initialize(repo_dir, redis_options = {})
  @dry_run = false
  @repo_dir = File.expand_path(repo_dir)
  raise "#{@repo_dir} does not exist!" unless Dir.exist? @repo_dir
  @redis = Redis.new(redis_options)
end

Instance Attribute Details

#dry_runObject

Returns the value of attribute dry_run.



90
91
92
# File 'lib/gitdis.rb', line 90

def dry_run
  @dry_run
end

#redisObject

Returns the value of attribute redis.



90
91
92
# File 'lib/gitdis.rb', line 90

def redis
  @redis
end

#repo_dirObject

Returns the value of attribute repo_dir.



90
91
92
# File 'lib/gitdis.rb', line 90

def repo_dir
  @repo_dir
end

Class Method Details

.concatenate(filenames) ⇒ Object

concatenate file contents into a single string separate by newlines, including CRs if any CRs are detected anywhere include a filetype-specific separator if recognized filenames is an array, and all lengths 0-N are handled



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/gitdis.rb', line 55

def self.concatenate(filenames)
  filetypes = filenames.map { |fname| File.extname(fname) }.uniq
  case filetypes.length
  when 0
    return "" if filenames.length == 0
    raise "filetype detection failure: #{filenames}"
  when 1
    sep = self.separator(filetypes.first)
  else
    raise "refusing to concatenate disparate filetypes: #{filetypes}"
  end

  newline = "\n"

  payload = filenames.map { |fname|
    contents = File.read(fname) || raise("could not read #{fname}")
    newline = "\r\n" if !newline.include?("\r") and contents.include?("\r")
    contents if !contents.empty?
  }.compact
  sep = sep.empty? ? newline : "#{newline}#{sep}#{newline}"
  payload.join(sep)
end

.dump(keys, redis_options) ⇒ Object



38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/gitdis.rb', line 38

def self.dump(keys, redis_options)
  redis = Redis.new(redis_options)
  keys.each { |base_key|
    self.keyset(base_key).each { |rkey|
      val = redis.get(rkey)
      if val and val.include?("\n")
        val = "\n" << val.split("\n").map { |line| "\t#{line}" }.join("\n")
        puts ["[#{rkey}]", val].join(' ')
      end
    }
  }
end

.exec(cmd, bytes = 1024) ⇒ Object

return Process::Status, stream through STDOUT and STDERR



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/gitdis.rb', line 7

def self.exec(cmd, bytes=1024)
  Open3.popen3(cmd) { |sin, sout, serr, thr|
    sin.close_write
    while !sout.eof or !serr.eof
      ready = IO.select [sout, serr]
      if ready
        ready[0].each { |f| # ready for reading
          begin
            (f == sout ? $stdout : $stderr).print f.read_nonblock(bytes)
          rescue EOFError => e
            # ok
          end
        }
      end
    end
    thr.value # Process::Status
  }
end

.exec!(cmd) ⇒ Object

raise on nonzero exit code



27
28
29
30
31
# File 'lib/gitdis.rb', line 27

def self.exec!(cmd)
  status = self.exec(cmd)
  raise "`#{cmd}` #{status}" unless status.exitstatus == 0
  0
end

.keyset(base_key) ⇒ Object

file contents, version, md5



34
35
36
# File 'lib/gitdis.rb', line 34

def self.keyset(base_key)
  [base_key, [base_key, 'version'].join(':'), [base_key, 'md5'].join(':')]
end

.separator(filetype) ⇒ Object

return a specific separator for known filetypes e.g. yaml document separator: —



80
81
82
83
84
85
86
87
88
# File 'lib/gitdis.rb', line 80

def self.separator(filetype)
  filetype = filetype[1..-1] if filetype[0] == '.'
  case filetype.downcase
  when 'yaml', 'yml'
    '---'
  else
    ''
  end
end

Instance Method Details

#git_pull(git_branch) ⇒ Object



99
100
101
102
103
104
105
106
107
108
# File 'lib/gitdis.rb', line 99

def git_pull(git_branch)
  Dir.chdir @repo_dir do
    if self.class.exec("git diff --quiet HEAD").exitstatus != 0
      raise "please stash your local changes"
    end
    self.class.exec! "git checkout #{git_branch}"
    self.class.exec! "git pull"
  end
  self
end

#update(base_key, relpath) ⇒ Object

e.g. update(‘foo:bar:baz’, ‘foo/bar/*.baz’) return nil # path does not exist

false      # no update needed
true       # update was needed, but just a dry run
[ver, md5] # updated


133
134
135
136
137
138
139
140
141
# File 'lib/gitdis.rb', line 133

def update(base_key, relpath)
  # handle e.g. "foo/bar/*.yaml"
  files = Dir.glob(File.join(@repo_dir, relpath))
  case files.length
  when 0 then nil
  when 1 then self.update_redis(base_key, File.read(files.first))
  else        self.update_redis(base_key, self.class.concatenate(files))
  end
end

#update_redis(base_key, file_contents) ⇒ Object

quick false if calculated md5 == redis md5 return true if dry run and update needed otherwise update contents and md5; increment version



113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/gitdis.rb', line 113

def update_redis(base_key, file_contents)
  md5 = Digest::MD5.hexdigest(file_contents)
  fkey, vkey, mkey = self.class.keyset(base_key)
  return false if @redis.get(mkey) == md5

  if @dry_run
    true
  else
    @redis.set(fkey, file_contents)
    @redis.set(mkey, md5)
    ver = @redis.incr(vkey)
    [ver, md5]
  end
end