Class: Upr::JSON

Inherits:
Struct
  • Object
show all
Includes:
Params
Defined in:
lib/upr/json.rb

Overview

JSON protocol based on Lighttpd’s mod_uploadprogress trac.lighttpd.net/trac/wiki/Docs:ModUploadProgress

Constant Summary collapse

INT_MAX =

We use this in case length is nil when clients send chunked uploads

0x7fffffff
SLEEP_CLASS =
defined?(Actor) ? Actor : Kernel
RESPONSE_HEADERS =

our default response headers, we need to set no-transform to prevent deflaters from compressing our already-small small input and also to prevent buffering/corking of the response inside deflater buffers.

{
  'Content-Type' => 'application/json',
  'Cache-Control' => 'no-cache, no-transform',
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Params

#extract_upload_id

Constructor Details

#initialize(options = {}) ⇒ JSON

Returns a new instance of JSON.



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

def initialize(options = {})
  super(options[:frequency] || 1, options[:backend], options[:upload_id])

  # support :drb for compatibility with mongrel_upload_progress
  if options[:drb]
    backend and raise ArgumentError, ":backend and :drb are incompatible"
    require 'drb'
    DRb.start_service
    self.backend = DRbObject.new(nil, options[:drb])
  elsif String === backend
    # allow people to use strings in case their backend gets
    # lazy-loaded (like an ActiveRecord model)
    self.backend = eval(backend)
  elsif backend.nil?
    raise ArgumentError, "backend MUST be specified"
  end

  # only for use with rails_proc
  upload_id.nil? and self.upload_id = options[:env]
end

Instance Attribute Details

#backendObject

Returns the value of attribute backend

Returns:

  • (Object)

    the current value of backend



13
14
15
# File 'lib/upr/json.rb', line 13

def backend
  @backend
end

#frequencyObject

Returns the value of attribute frequency

Returns:

  • (Object)

    the current value of frequency



13
14
15
# File 'lib/upr/json.rb', line 13

def frequency
  @frequency
end

#upload_idObject

Returns the value of attribute upload_id

Returns:

  • (Object)

    the current value of upload_id



13
14
15
# File 'lib/upr/json.rb', line 13

def upload_id
  @upload_id
end

Instance Method Details

#_error_msg(msg) ⇒ Object



126
127
128
# File 'lib/upr/json.rb', line 126

def _error_msg(msg)
  _json_object(:state => 'error', :status => 400, :message => msg)
end

#_json_object(options) ⇒ Object



130
131
132
133
# File 'lib/upr/json.rb', line 130

def _json_object(options)
  # $stderr.syswrite "#{options.inspect} #{$upr.inspect}\n"
  options.to_json
end

#_onceObject



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

def _once
  if status = backend.read(upload_id)
    if status.done?
      _json_object(:state => 'done')
    elsif status.seen == 0
      _json_object(:state => 'starting')
    elsif status.error?
      _error_msg("upload failed")
    else
      _update_msg(status)
    end
  else
    timeout = Time.now + 2
    until status = backend.read(upload_id)
      SLEEP_CLASS.sleep(0.1)
      return _error_msg("couldn't get status") if Time.now > timeout
    end
    _json_object(:state => 'starting')
  end
end

#_update_msg(status) ⇒ Object



135
136
137
138
139
140
# File 'lib/upr/json.rb', line 135

def _update_msg(status)
  raise "client error" if status.error?
  received = status.seen
  size = status.length || INT_MAX
  _json_object(:state => 'uploading', :size => size, :received => received)
end

#_wrap(env, uid) ⇒ Object

Rack interface reservced for future use with streaming AJAX



120
121
122
123
124
# File 'lib/upr/json.rb', line 120

def _wrap(env, uid)
  _self = dup
  _self.upload_id = uid
  [ 200, RESPONSE_HEADERS.dup, _self ]
end

#call(env) ⇒ Object

Rack interface reservced for future use with streaming AJAX



85
86
87
88
89
90
91
# File 'lib/upr/json.rb', line 85

def call(env)
  if uid = extract_upload_id(env)
    _wrap(env, uid)
  else
    [ 400, RESPONSE_HEADERS.dup, [ _error_msg("upload_id not given") ] ]
  end
end

#each {|_json_object(:state => 'starting') << eol| ... } ⇒ Object

Rack interface reservced for future use with streaming AJAX

Yields:



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/upr/json.rb', line 94

def each(&block)
  sleeper = defined?(Actor) ? Actor : Kernel
  timeout = Time.now + 2
  eol = ";\n"
  yield _json_object(:state => 'starting') << eol
  begin
    until status = backend.read(upload_id)
      sleeper.sleep(0.1)
      break if Time.now > timeout
    end
    if status
      begin
        yield _update_msg(status) << eol
        break if status.done?
        sleeper.sleep(frequency)
      end while status = backend.read(upload_id)
      yield _json_object(:state => 'done') << eol
    else
      yield _error_msg("couldn't get status") << eol
    end
  rescue => e
    yield _error_msg(e.message) << eol
  end
end

#rails_render_optionsObject



52
53
54
55
56
57
58
59
60
61
# File 'lib/upr/json.rb', line 52

def rails_render_options
  env = upload_id
  self.upload_id = extract_upload_id(env)
  text = if Rack::Request.new(env).GET.include?("long")
    Proc.new { |response,output| each { |line| output.write(line) } }
  else
    _once
  end
  { :content_type => 'application/json', :text => text }
end