Class: Net::GitHub::Upload

Inherits:
Object
  • Object
show all
Defined in:
lib/net/github-upload.rb

Constant Summary collapse

VERSION =
'0.0.8'

Instance Method Summary collapse

Constructor Details

#initialize(params = nil) ⇒ Upload

Returns a new instance of Upload.



12
13
14
15
16
17
18
19
# File 'lib/net/github-upload.rb', line 12

def initialize params=nil
  @login = params[:login]
  @token = params[:token]

  if @login.empty? or @token.empty?
    raise "login or token is empty"
  end
end

Instance Method Details

#delete(repos, id) ⇒ Object

Delete an individual file (used by #replace when replacing existing files).



139
140
141
142
143
144
145
# File 'lib/net/github-upload.rb', line 139

def delete repos, id
  HTTPClient.post("https://github.com/#{repos}/downloads/#{id.gsub( "download_", '')}", {
    "_method"      => "delete",
    "login"        => @login,
    "token"        => @token
  })
end

#delete_all(repos) ⇒ Object

Delete all uploaded files.



128
129
130
131
132
133
134
135
136
# File 'lib/net/github-upload.rb', line 128

def delete_all repos
  unless repos
    raise "required repository name"
  end
  repos = @login + '/' + repos unless repos.include? '/'
  list_files(repos).each { |obj|
    delete repos, obj[:id]
  }
end

#list_files(repos) ⇒ Object

List all the files uploaded to a repository.



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/net/github-upload.rb', line 148

def list_files repos
  raise "required repository name" unless repos
  res = HTTPClient.get_content("https://github.com/#{repos}/downloads", {
    "login" => @login,
    "token" => @token
  })
  Nokogiri::HTML(res).xpath('id("manual_downloads")/li').map do |fileinfo|
    obj = {
      :description =>  fileinfo.at_xpath('descendant::h4').text.force_encoding('BINARY').gsub(/.+?\xe2\x80\x94 (.+?)(\n\s*)?$/m, '\1'),
      :date => fileinfo.at_xpath('descendant::p/time').attribute('title').text,
      :size => fileinfo.at_xpath('descendant::p/strong').text,
      :id => /\d+$/.match(fileinfo.at_xpath('a').attribute('href').text)[0]
    }
    anchor = fileinfo.at_xpath('descendant::h4/a')
    obj[:link] = anchor.attribute('href').text
    obj[:name] = anchor.text
    obj
  end
end

#replace(info = {}, &block) ⇒ Object

Upload a file and replace it if it already exists on the server.

See Also:



123
124
125
# File 'lib/net/github-upload.rb', line 123

def replace info = {}, &block
   upload info.merge( :replace => true ), &block
end

#upload(info) { ... } ⇒ Object

Upload a file to github. Will fail if the file already exists. To upload, either supply :data and :name or :file.

Parameters:

  • info (Hash)

Options Hash (info):

  • :content_type (String) — default: 'application/octet-stream'

    Type of data to send

  • :data (String)

    Data to upload as a file. Requires that the :name option is set.

  • :description (String) — default: ''

    Description of file on Github download page.

  • :file (String)

    Path to file to upload.

  • :name (String) — default: filename of info[:file] if uploading a file

    Name the file will have (not including path) when uploaded.

  • :replace (Boolean) — default: false

    True to force overwriting of an existing file.

  • :repos (String)

    Name of Github project, such as “my_project”, which is the repository.

  • :upload_timeout (Float) — default: 120

    Maximum time, in seconds, before abandoning a file upload.

  • :yield_interval (Float) — default: 1

    Interval, in seconds, between yields if block is given.

Yields:

  • Optional block will yield every info seconds (This can be used, for example, to print “#” every second so users see that the upload is continuing).



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/net/github-upload.rb', line 35

def upload info
  unless info[:repos]
    raise "required repository name"
  end
  info[:repos] = @login + '/' + info[:repos] unless info[:repos].include? '/'

  if info[:file]
    file = info[:file]
    unless File.exist?(file) && File.readable?(file)
      raise "file does not exsits or readable"
    end
    info[:name] ||= File.basename(file)
  end
  unless  info[:file] || info[:data]
    raise "required file or data parameter to upload"
  end

  unless info[:name]
    raise "required name parameter for filename with data parameter"
  end

  if info[:replace]
    list_files(info[:repos]).each { |obj|
      next unless obj[:name] == info[:name]
      delete info[:repos], obj[:id]
    }
  elsif list_files(info[:repos]).any?{|obj| obj[:name] == info[:name]}
    raise "file '#{info[:name]}' is already uploaded. please try different name"
  end

  info[:content_type] ||= 'application/octet-stream'
  stat = HTTPClient.post("https://github.com/#{info[:repos]}/downloads", {
    "file_size"    => info[:file] ? File.stat(info[:file]).size : info[:data].size,
    "content_type" => info[:content_type],
    "file_name"    => info[:name],
    "description"  => info[:description] || '',
    "login"        => @login,
    "token"        => @token
  })

  unless stat.code == 200
    raise "Failed to post file info"
  end

  upload_info = JSON.parse(stat.content)
  if info[:file]
    f = File.open(info[:file], 'rb')
  else
    f = Tempfile.open('net-github-upload')
    f << info[:data]
    f.flush
  end
  client = HTTPClient.new
  client.send_timeout = info[:upload_timeout] if info[:upload_timeout]

  res = begin
    connection = client.post_async("http://github.s3.amazonaws.com/", [
        ['Filename', info[:name]],
        ['policy', upload_info['policy']],
        ['success_action_status', 201],
        ['key', upload_info['path']],
        ['AWSAccessKeyId', upload_info['accesskeyid']],
        ['Content-Type', upload_info['content_type'] || 'application/octet-stream'],
        ['signature', upload_info['signature']],
        ['acl', upload_info['acl']],
        ['file', f]
    ])

    until connection.finished?
      yield if block_given?
      sleep info[:yield_interval] || 1
    end

    connection.pop
  ensure
    f.close
  end

  if res.status == 201
    return FasterXmlSimple.xml_in(res.body.read)['PostResponse']['Location']
  else
    raise 'Failed to upload' + extract_error_message(res.body)
  end
end