helios 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +50 -21
  3. data/README.md +27 -2
  4. data/helios.gemspec +10 -6
  5. data/lib/helios.rb +10 -4
  6. data/lib/helios/backend.rb +29 -22
  7. data/lib/helios/backend/data.rb +10 -5
  8. data/lib/helios/backend/in-app-purchase.rb +4 -2
  9. data/lib/helios/backend/newsstand.rb +97 -0
  10. data/lib/helios/backend/passbook.rb +8 -2
  11. data/lib/helios/backend/push-notification.rb +25 -12
  12. data/lib/helios/commands/console.rb +4 -10
  13. data/lib/helios/commands/new.rb +0 -1
  14. data/lib/helios/commands/server.rb +4 -1
  15. data/lib/helios/frontend.rb +6 -14
  16. data/lib/helios/frontend/images/helios.svg +33 -0
  17. data/lib/helios/frontend/javascripts/helios.coffee +39 -3
  18. data/lib/helios/frontend/javascripts/helios/collections.coffee +10 -2
  19. data/lib/helios/frontend/javascripts/helios/models.coffee +4 -1
  20. data/lib/helios/frontend/javascripts/helios/router.coffee +13 -1
  21. data/lib/helios/frontend/javascripts/helios/views.coffee +82 -8
  22. data/lib/helios/frontend/javascripts/vendor/jquery/jquery.fileupload-ui.js +807 -0
  23. data/lib/helios/frontend/javascripts/vendor/jquery/jquery.fileupload.js +1201 -0
  24. data/lib/helios/frontend/javascripts/vendor/jquery/jquery.ui.widget.js +530 -0
  25. data/lib/helios/frontend/javascripts/vendor/linkheaders.js +117 -0
  26. data/lib/helios/frontend/stylesheets/_iphone.sass +1 -1
  27. data/lib/helios/frontend/stylesheets/screen.sass +2 -6
  28. data/lib/helios/frontend/templates/{entities.jst.tpl → data/entities.jst.tpl} +0 -0
  29. data/lib/helios/frontend/templates/{receipts.jst.tpl → in-app-purchase/receipts.jst.tpl} +0 -0
  30. data/lib/helios/frontend/templates/navigation.jst.tpl +31 -0
  31. data/lib/helios/frontend/templates/newsstand/issues.jst.tpl +16 -0
  32. data/lib/helios/frontend/templates/newsstand/new.jst.tpl +28 -0
  33. data/lib/helios/frontend/templates/{passes.jst.tpl → passbook/passes.jst.tpl} +0 -0
  34. data/lib/helios/frontend/templates/{compose.jst.tpl → push-notification/compose.jst.tpl} +0 -0
  35. data/lib/helios/frontend/templates/{devices.jst.tpl → push-notification/devices.jst.tpl} +0 -0
  36. data/lib/helios/frontend/views/index.haml +4 -28
  37. data/lib/helios/templates/.env.erb +1 -0
  38. data/lib/helios/templates/.gitignore +3 -0
  39. data/lib/helios/version.rb +1 -1
  40. metadata +86 -20
  41. data/lib/helios/frontend/stylesheets/_bariol.scss +0 -41
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 402e29639eacaa1ba54ba2f4eccefb1ac228be91
4
- data.tar.gz: c2f8542dd3218c7a2b97c75aeea6a0cec5c57f76
3
+ metadata.gz: 804589d0f2e0ada259f0b9fed0746db10843ef8c
4
+ data.tar.gz: 05599e5a5f8d21b616dd6ae7e0cfdfd2cf33e34b
5
5
  SHA512:
6
- metadata.gz: 4bfd61221daf7968ccba1a273930dfc0787610ce175e2d2cc0fab8e30fa7828b27c30810d8acf0597e9f5d09239b9ca6d974ad46c3017eb76b758bc214bae877
7
- data.tar.gz: 2f8bb05d75f2bef9f6b4fb1f308aa7741bba846a6292aeb6be94b2c58e60d9e1199d919dc8afc838d68e179af1ad39795d60ea370c3daf3bd09376e68750a8d5
6
+ metadata.gz: 12d3ceb7bf25ea1040f3ecb0faffbe2dd433a4733c407cb4f86ccb43db471d623759a678089f10f4f45740ec1dc1d9f3e90b1b07dd393aaae684da4944632053
7
+ data.tar.gz: 58e382a519b93419ee86cb8a195c80fed29693bbe2ae0f588355dab9e7f22629be19b6fb1f870b45ca711fc72a7355900951a59b593e357aefb1967084ae976f
data/Gemfile.lock CHANGED
@@ -1,22 +1,26 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- helios (0.2.1)
4
+ helios (0.2.2)
5
5
  coffee-script (~> 2.2)
6
6
  commander (~> 4.1)
7
7
  compass (~> 0.12)
8
+ core_data
9
+ fog (~> 1.10)
8
10
  foreman (~> 0.63)
9
11
  haml (>= 3.1)
12
+ houston (~> 0.2)
10
13
  json (~> 1.7)
11
14
  rack-contrib (~> 1.1)
12
- rack-core-data (~> 0.3)
13
15
  rack-in-app-purchase (~> 0.1)
16
+ rack-newsstand (~> 0.1)
14
17
  rack-passbook (~> 0.1)
15
18
  rack-push-notification (~> 0.4)
19
+ rack-scaffold
16
20
  rails-database-url (~> 1.0)
17
21
  sinatra (~> 1.3)
18
- sinatra-assetpack (~> 0.1.7)
19
- sinatra-backbone (~> 0.1.0.rc2)
22
+ sinatra-assetpack (~> 0.2.2)
23
+ sinatra-backbone (~> 0.1.1)
20
24
  sinatra-contrib (~> 1.3)
21
25
  sinatra-param (~> 0.1)
22
26
  sinatra-support (~> 1.2)
@@ -29,6 +33,7 @@ GEM
29
33
  i18n (= 0.6.1)
30
34
  multi_json (~> 1.0)
31
35
  backports (3.3.0)
36
+ builder (3.2.0)
32
37
  chunky_png (1.2.8)
33
38
  coffee-script (2.2.0)
34
39
  coffee-script-source
@@ -40,55 +45,77 @@ GEM
40
45
  chunky_png (~> 1.2)
41
46
  fssm (>= 0.2.7)
42
47
  sass (~> 3.1)
48
+ core_data (0.1.1)
49
+ nokogiri (~> 1.4)
43
50
  diff-lcs (1.2.4)
44
51
  dotenv (0.7.0)
45
52
  eventmachine (1.0.3)
53
+ excon (0.21.0)
46
54
  execjs (1.4.0)
47
55
  multi_json (~> 1.0)
56
+ fog (1.11.1)
57
+ builder
58
+ excon (~> 0.20)
59
+ formatador (~> 0.2.0)
60
+ json (~> 1.7)
61
+ mime-types
62
+ net-scp (~> 1.1)
63
+ net-ssh (>= 2.1.3)
64
+ nokogiri (~> 1.5.0)
65
+ ruby-hmac
48
66
  foreman (0.63.0)
49
67
  dotenv (>= 0.7)
50
68
  thor (>= 0.13.6)
69
+ formatador (0.2.4)
51
70
  fssm (0.2.10)
52
71
  haml (4.0.2)
53
72
  tilt
54
73
  highline (1.6.18)
55
- houston (0.1.1)
56
- commander (~> 4.1.2)
57
- json (~> 1.7.3)
74
+ houston (0.2.2)
75
+ commander (~> 4.1)
76
+ json
58
77
  i18n (0.6.1)
59
78
  jsmin (1.0.1)
60
79
  json (1.7.7)
61
- multi_json (1.7.2)
80
+ mime-types (1.23)
81
+ multi_json (1.7.3)
82
+ net-scp (1.1.0)
83
+ net-ssh (>= 2.6.5)
84
+ net-ssh (2.6.7)
62
85
  nokogiri (1.5.9)
86
+ plist (3.1.0)
63
87
  rack (1.5.2)
64
88
  rack-contrib (1.1.0)
65
89
  rack (>= 0.9.1)
66
- rack-core-data (0.3.1)
67
- activesupport (>= 3.0)
68
- nokogiri (~> 1.4)
90
+ rack-in-app-purchase (0.1.0)
69
91
  rack (~> 1.4)
70
- rack-contrib (~> 1.1)
71
92
  sequel (~> 3.37)
72
93
  sinatra (~> 1.3)
73
- sinatra-param (~> 0.1)
74
- rack-in-app-purchase (0.1.0)
94
+ venice
95
+ rack-newsstand (0.1.1)
96
+ builder (>= 3.0)
97
+ plist (~> 3.1)
75
98
  rack (~> 1.4)
76
99
  sequel (~> 3.37)
77
100
  sinatra (~> 1.3)
78
- venice
79
101
  rack-passbook (0.1.1)
80
102
  rack (~> 1.4)
81
103
  sequel (~> 3.37)
82
104
  sinatra (~> 1.3)
83
105
  rack-protection (1.5.0)
84
106
  rack
85
- rack-push-notification (0.4.0)
86
- houston (~> 0.1.1)
107
+ rack-push-notification (0.4.1)
87
108
  rack (~> 1.4)
88
109
  rack-contrib (~> 1.1)
89
110
  sequel (~> 3.37)
90
111
  sinatra (~> 1.3)
91
112
  sinatra-param (~> 0.1)
113
+ rack-scaffold (0.0.1)
114
+ activesupport (>= 3.0)
115
+ rack (~> 1.4)
116
+ rack-contrib (~> 1.1)
117
+ sinatra (~> 1.3)
118
+ sinatra-param (~> 0.1)
92
119
  rack-test (0.6.2)
93
120
  rack (>= 1.0)
94
121
  rails-database-url (1.0.0)
@@ -101,13 +128,15 @@ GEM
101
128
  rspec-expectations (2.13.0)
102
129
  diff-lcs (>= 1.1.3, < 2.0)
103
130
  rspec-mocks (2.13.1)
104
- sass (3.2.8)
105
- sequel (3.46.0)
131
+ ruby-hmac (0.4.0)
132
+ sass (3.2.9)
133
+ sequel (3.47.0)
106
134
  sinatra (1.4.2)
107
135
  rack (~> 1.5, >= 1.5.2)
108
136
  rack-protection (~> 1.4)
109
137
  tilt (~> 1.3, >= 1.3.4)
110
- sinatra-assetpack (0.1.7)
138
+ sinatra-assetpack (0.2.2)
139
+ backports
111
140
  jsmin
112
141
  rack-test
113
142
  sinatra
@@ -127,7 +156,7 @@ GEM
127
156
  sinatra (>= 1.0)
128
157
  terminal-table (1.4.5)
129
158
  thor (0.18.1)
130
- tilt (1.3.7)
159
+ tilt (1.4.1)
131
160
  venice (0.1.0)
132
161
  commander (~> 4.1)
133
162
  json
data/README.md CHANGED
@@ -76,7 +76,7 @@ Bundler.require
76
76
 
77
77
  run Helios::Application.new do
78
78
  service :data, model: 'path/to/DataModel.xcdatamodel'
79
- service :push_notification
79
+ service :push_notification, apn_certificate: 'path/to/apple_push_notification.pem', apn_environment: 'development'
80
80
  service :in_app_purchase
81
81
  service :passbook
82
82
  end
@@ -102,7 +102,7 @@ Helios can be run as Rails middleware by adding this to the configuration block
102
102
  ```ruby
103
103
  config.middleware.use Helios::Application do
104
104
  service :data, model: 'path/to/DataModel.xcdatamodel'
105
- service :push_notification
105
+ service :push_notification, apn_certificate: 'path/to/apple_push_notification.pem', apn_environment: 'development'
106
106
  service :in_app_purchase
107
107
  service :passbook
108
108
  end
@@ -164,6 +164,10 @@ Each entity in the specified data model will have a `Sequel::Model` subclass cre
164
164
  <td><tt>DELETE /devices/:token</tt></td>
165
165
  <td>Unregister a device from receiving push notifications</td>
166
166
  </tr>
167
+ <tr>
168
+ <td><tt>POST /message</tt></td>
169
+ <td>Send out a push notification to some devices</td>
170
+ </tr>
167
171
  </table>
168
172
 
169
173
  ---
@@ -266,6 +270,12 @@ To run Helios in development mode on `localhost`, run the `server` command:
266
270
 
267
271
  $ helios server
268
272
 
273
+ ### Testing Push Notifications
274
+
275
+ Once you have registered a device and set up your certificate, try this:
276
+
277
+ $ curl -X POST -d 'payload={"aps": {"alert":"Blastoff!"}}' http://localhost:5000/message
278
+
269
279
  ### Running the Helios Console
270
280
 
271
281
  You can start an IRB session with the runtime environment of the Helios application with the `console` command:
@@ -311,6 +321,21 @@ didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
311
321
  }
312
322
  ```
313
323
 
324
+ ### Converting Your Push Notification Certificate
325
+
326
+ > These instructions come from the [APN on Rails](https://github.com/PRX/apn_on_rails) project.
327
+
328
+ Once you have the certificate from Apple for your application, export your key
329
+ and the apple certificate as p12 files. Here is a quick walkthrough on how to do this:
330
+
331
+ 1. Click the disclosure arrow next to your certificate in Keychain Access and select the certificate and the key.
332
+ 2. Right click and choose `Export 2 items…`.
333
+ 3. Choose the p12 format from the drop down and name it `cert.p12`.
334
+
335
+ Now covert the p12 file to a pem file:
336
+
337
+ $ openssl pkcs12 -in cert.p12 -out apple_push_notification.pem -nodes -clcerts
338
+
314
339
  ## Coming Attractions
315
340
 
316
341
  There's still a lot to do to make Helios even better. Here are some ideas that are at the top of the list:
data/helios.gemspec CHANGED
@@ -16,27 +16,31 @@ Gem::Specification.new do |s|
16
16
  s.add_dependency "commander", "~> 4.1"
17
17
  s.add_dependency "foreman", "~> 0.63"
18
18
  s.add_dependency "rack-contrib", "~> 1.1"
19
- s.add_dependency "rack-core-data", "~> 0.3"
20
19
  s.add_dependency "rack-push-notification", "~> 0.4"
21
20
  s.add_dependency "rack-in-app-purchase", "~> 0.1"
22
21
  s.add_dependency "rack-passbook", "~> 0.1"
22
+ s.add_dependency "rack-newsstand", "~> 0.1"
23
+ s.add_dependency "rack-scaffold"
24
+ s.add_dependency "core_data"
23
25
  s.add_dependency "json", "~> 1.7"
24
26
  s.add_dependency "coffee-script", "~> 2.2"
25
27
  s.add_dependency "sinatra", "~> 1.3"
26
28
  s.add_dependency "sinatra-contrib", "~> 1.3"
27
- s.add_dependency "sinatra-assetpack", "~> 0.1.7"
28
- s.add_dependency "sinatra-backbone", "~> 0.1.0.rc2"
29
+ s.add_dependency "sinatra-assetpack", "~> 0.2.2"
30
+ s.add_dependency "sinatra-backbone", "~> 0.1.1"
29
31
  s.add_dependency "sinatra-param", "~> 0.1"
30
32
  s.add_dependency "sinatra-support", "~> 1.2"
31
33
  s.add_dependency "haml", ">= 3.1"
32
34
  s.add_dependency "compass", "~> 0.12"
33
35
  s.add_dependency "zurb-foundation", "4.1.2"
34
36
  s.add_dependency "rails-database-url", "~> 1.0"
37
+ s.add_dependency "fog", "~> 1.10"
38
+ s.add_dependency "houston", "~> 0.2"
35
39
 
36
- s.add_development_dependency "rake", ">= 0"
37
- s.add_development_dependency "rspec", ">= 0"
40
+ s.add_development_dependency "rake"
41
+ s.add_development_dependency "rspec"
38
42
 
39
- s.files = Dir["./**/*"].reject{|file| file =~ /\.\/(bin|example|log|pkg|script|spec|test|vendor)/} + Dir.glob("./lib/helios/templates", File::FNM_DOTMATCH)
43
+ s.files = Dir["./**/*"].reject{|file| file =~ /\.\/(bin|example|log|pkg|script|spec|test|vendor)/} + Dir.glob("./lib/helios/templates/*", File::FNM_DOTMATCH)
40
44
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
41
45
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
42
46
  s.require_paths = ["lib"]
data/lib/helios.rb CHANGED
@@ -3,11 +3,17 @@ require 'rack'
3
3
  module Helios
4
4
  class Application
5
5
  def initialize(app = nil, options = {}, &block)
6
- map = {}
7
- map['/'] = Rack::Cascade.new([app, Helios::Backend.new(&block)].compact)
8
- map['/admin'] = Helios::Frontend.new if options.fetch(:frontend, true)
6
+ @app = Rack::Builder.new do
7
+ map '/admin' do
8
+ use Rack::Auth::Basic, "Restricted Area" do |username, password|
9
+ username == (ENV['HELIOS_ADMIN_USERNAME'] || "") and password == (ENV['HELIOS_ADMIN_PASSWORD'] || "")
10
+ end if ENV['HELIOS_ADMIN_USERNAME'] or ENV['HELIOS_ADMIN_PASSWORD']
9
11
 
10
- @app = Rack::URLMap.new(map)
12
+ run Helios::Frontend.new
13
+ end
14
+
15
+ run Helios::Backend.new(&block)
16
+ end
11
17
  end
12
18
 
13
19
  def call(env)
@@ -1,22 +1,31 @@
1
1
  require 'rack'
2
2
 
3
3
  module Helios
4
- class Backend < Rack::Cascade
4
+ class Backend < Rack::Builder
5
+ DEFAULT_PATHS = {
6
+ data: '/'
7
+ }
8
+
5
9
  require 'rails-database-url' if const_defined?(:Rails)
6
10
 
7
- def initialize(&block)
8
- @services = []
11
+ def initialize(*args, &block)
12
+ raise ArgumentError, "Missing block" unless block_given?
13
+ super(&nil)
9
14
 
10
- block = lambda { |app|
11
- service :data, model: Dir['**/*.xcdatamodeld'].first rescue false
12
- service :push_notification
13
- service :in_app_purchase
14
- service :passbook
15
- } unless block_given?
15
+ @services = {}
16
16
 
17
17
  instance_eval(&block)
18
+ end
18
19
 
19
- super(@services)
20
+ def call(env)
21
+ return super(env) unless env["REQUEST_METHOD"] == "OPTIONS" and env["REQUEST_PATH"] == "/"
22
+
23
+ links = []
24
+ @services.each do |path, middleware|
25
+ links << %{<#{path}>; rel="#{middleware}"}
26
+ end
27
+
28
+ [206, {"Link" => links.join("\n")}, []]
20
29
  end
21
30
 
22
31
  private
@@ -28,30 +37,28 @@ module Helios
28
37
  begin
29
38
  middleware = Helios::Backend.const_get(constantize(identifier))
30
39
  rescue NameError
31
- raise LoadError, "Could not find matching service for #{identifier.inspect}. You may need to install an additional gem (such as helios-#{identifier})."
40
+ raise LoadError, "Could not find matching service for #{identifier.inspect} (Helios::Backend::#{constantize(identifier)}). You may need to install an additional gem (such as helios-#{identifier})."
32
41
  end
33
42
  end
34
43
 
35
- middleware.instance_eval{ include Helios::Administerable } if options.fetch(:frontend, true)
44
+ path = "/#{(options.delete(:root) || DEFAULT_PATHS[identifier] || identifier)}".squeeze("/")
45
+
46
+ map path do
47
+ instance_eval(&block) if block_given?
48
+ run middleware.new(self, options)
49
+ end
36
50
 
37
- @services << middleware.new(self, options, &block) if middleware
51
+ @services[path] = middleware
38
52
  end
39
53
 
40
54
  def constantize(identifier)
41
55
  identifier.to_s.split(/([[:alpha:]]*)/).select{|c| /[[:alpha:]]/ === c}.map(&:capitalize).join("")
42
56
  end
43
57
  end
44
-
45
- module Administerable
46
- attr_accessor :admin
47
-
48
- def admin?
49
- !!@admin
50
- end
51
- end
52
58
  end
53
59
 
54
60
  require 'helios/backend/data'
55
- require 'helios/backend/push-notification'
56
61
  require 'helios/backend/in-app-purchase'
57
62
  require 'helios/backend/passbook'
63
+ require 'helios/backend/push-notification'
64
+ require 'helios/backend/newsstand'
@@ -1,21 +1,26 @@
1
- require 'rack/core-data'
1
+ require 'core_data'
2
+ require 'sequel'
3
+
4
+ require 'rack/scaffold'
5
+
6
+ require 'sinatra/base'
2
7
  require 'sinatra/param'
3
8
 
9
+
4
10
  class Helios::Backend::Data < Sinatra::Base
5
11
  helpers Sinatra::Param
6
12
 
7
13
  def initialize(app, options = {})
8
- super(Rack::CoreData(options[:model]))
14
+ super(Rack::Scaffold.new(options))
9
15
 
10
- @model = Rack::CoreData::DataModel.new(options[:model])
16
+ @model = CoreData::DataModel.new(options[:model]) rescue nil
11
17
  end
12
18
 
13
19
  before do
14
20
  content_type :json
15
21
  end
16
22
 
17
- options '/' do
18
- pass unless self.class < Helios::Administerable
23
+ options '/resources' do
19
24
 
20
25
  links = []
21
26
  @model.entities.each do |entity|
@@ -1,13 +1,15 @@
1
1
  require 'rack/in-app-purchase'
2
+
3
+ require 'sinatra/base'
2
4
  require 'sinatra/param'
3
5
 
4
6
  class Helios::Backend::InAppPurchase < Sinatra::Base
5
7
  helpers Sinatra::Param
6
8
 
7
- def initialize(app, options = {})
9
+ def initialize(app, options = {}, &block)
8
10
  super(Rack::InAppPurchase.new)
9
11
  end
10
-
12
+
11
13
  before do
12
14
  content_type :json
13
15
  end
@@ -0,0 +1,97 @@
1
+ require 'rack/newsstand'
2
+
3
+ require 'sinatra/base'
4
+ require 'sinatra/param'
5
+
6
+ require 'fog'
7
+
8
+ class Helios::Backend::Newsstand < Sinatra::Base
9
+ helpers Sinatra::Param
10
+
11
+ def initialize(app, options = {}, &block)
12
+ super(Rack::Newsstand.new)
13
+
14
+ @storage = Fog::Storage.new(options[:storage]) if options[:storage]
15
+ end
16
+
17
+ before do
18
+ content_type :json
19
+ end
20
+
21
+ get '/issues/?' do
22
+ pass unless request.accept? 'application/json'
23
+
24
+ param :q, String
25
+
26
+ issues = Rack::Newsstand::Issue.dataset
27
+ issues = issues.filter("tsv @@ to_tsquery('english', ?)", "#{params[:q]}:*") if params[:q] and not params[:q].empty?
28
+
29
+ if params[:page] or params[:per_page]
30
+ param :page, Integer, default: 1, min: 1
31
+ param :per_page, Integer, default: 100, in: (1..100)
32
+
33
+ {
34
+ issues: issues.limit(params[:per_page], (params[:page] - 1) * params[:per_page]).naked.all,
35
+ page: params[:page],
36
+ total: issues.count
37
+ }.to_json
38
+ else
39
+ param :limit, Integer, default: 100, in: (1..100)
40
+ param :offset, Integer, default: 0, min: 0
41
+
42
+ {
43
+ issues: issues.limit(params[:limit], params[:offset]).naked.all
44
+ }.to_json
45
+ end
46
+ end
47
+
48
+ head '/issues/new?' do
49
+ status 503 and return unless @storage
50
+
51
+ status 204
52
+ end
53
+
54
+ post '/issues/?' do
55
+ status 503 and return unless @storage
56
+
57
+ param :name, String, empty: false
58
+ param :summary, String
59
+
60
+ issue = Rack::Newsstand::Issue.new(params)
61
+
62
+ if issue.valid?
63
+ directory = @storage.directories.create(key: "newsstand-issue-#{issue.name}-#{Time.now.to_i}", public: true)
64
+
65
+ covers, assets = {}, []
66
+ [:covers, :assets].each do |attribute|
67
+ (params[attribute] || []).each do |f|
68
+ file = directory.files.create(
69
+ key: File.basename(f[:filename]),
70
+ body: File.open(f[:tempfile]),
71
+ public: true
72
+ )
73
+
74
+ case attribute
75
+ when :covers
76
+ covers["SOURCE"] = file.public_url
77
+ when :assets
78
+ assets << file.public_url
79
+ end
80
+ end
81
+ end
82
+
83
+ issue.set(cover_urls: covers, asset_urls: assets)
84
+
85
+ if issue.save
86
+ status 201
87
+ issue.to_json
88
+ else
89
+ status 400
90
+ {errors: issue.errors}.to_json
91
+ end
92
+ else
93
+ status 400
94
+ {errors: issue.errors}.to_json
95
+ end
96
+ end
97
+ end