helios_aim 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. checksums.yaml +15 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +173 -0
  4. data/LICENSE +19 -0
  5. data/README.md +363 -0
  6. data/Rakefile +10 -0
  7. data/bin/helios +20 -0
  8. data/helios_aim.gemspec +47 -0
  9. data/lib/helios.rb +27 -0
  10. data/lib/helios/backend.rb +64 -0
  11. data/lib/helios/backend/data.rb +40 -0
  12. data/lib/helios/backend/in-app-purchase.rb +43 -0
  13. data/lib/helios/backend/newsstand.rb +97 -0
  14. data/lib/helios/backend/passbook.rb +41 -0
  15. data/lib/helios/backend/push-notification.rb +110 -0
  16. data/lib/helios/commands.rb +4 -0
  17. data/lib/helios/commands/console.rb +21 -0
  18. data/lib/helios/commands/link.rb +19 -0
  19. data/lib/helios/commands/new.rb +76 -0
  20. data/lib/helios/commands/server.rb +52 -0
  21. data/lib/helios/frontend.rb +66 -0
  22. data/lib/helios/frontend/fonts/icons.eot +0 -0
  23. data/lib/helios/frontend/fonts/icons.ttf +0 -0
  24. data/lib/helios/frontend/fonts/icons.woff +0 -0
  25. data/lib/helios/frontend/images/bg.jpg +0 -0
  26. data/lib/helios/frontend/images/helios.svg +33 -0
  27. data/lib/helios/frontend/javascripts/helios.coffee +72 -0
  28. data/lib/helios/frontend/javascripts/helios/collections.coffee +99 -0
  29. data/lib/helios/frontend/javascripts/helios/models.coffee +23 -0
  30. data/lib/helios/frontend/javascripts/helios/router.coffee +50 -0
  31. data/lib/helios/frontend/javascripts/helios/views.coffee +307 -0
  32. data/lib/helios/frontend/javascripts/vendor/backbone.datagrid.js +662 -0
  33. data/lib/helios/frontend/javascripts/vendor/backbone.js +1487 -0
  34. data/lib/helios/frontend/javascripts/vendor/backbone.paginator.js +1046 -0
  35. data/lib/helios/frontend/javascripts/vendor/codemirror.javascript.js +411 -0
  36. data/lib/helios/frontend/javascripts/vendor/codemirror.js +3047 -0
  37. data/lib/helios/frontend/javascripts/vendor/date.js +104 -0
  38. data/lib/helios/frontend/javascripts/vendor/foundation.js +331 -0
  39. data/lib/helios/frontend/javascripts/vendor/foundation/foundation.alerts.js +50 -0
  40. data/lib/helios/frontend/javascripts/vendor/foundation/foundation.clearing.js +478 -0
  41. data/lib/helios/frontend/javascripts/vendor/foundation/foundation.cookie.js +74 -0
  42. data/lib/helios/frontend/javascripts/vendor/foundation/foundation.dropdown.js +122 -0
  43. data/lib/helios/frontend/javascripts/vendor/foundation/foundation.forms.js +403 -0
  44. data/lib/helios/frontend/javascripts/vendor/foundation/foundation.joyride.js +613 -0
  45. data/lib/helios/frontend/javascripts/vendor/foundation/foundation.magellan.js +130 -0
  46. data/lib/helios/frontend/javascripts/vendor/foundation/foundation.orbit.js +355 -0
  47. data/lib/helios/frontend/javascripts/vendor/foundation/foundation.placeholder.js +159 -0
  48. data/lib/helios/frontend/javascripts/vendor/foundation/foundation.reveal.js +272 -0
  49. data/lib/helios/frontend/javascripts/vendor/foundation/foundation.section.js +183 -0
  50. data/lib/helios/frontend/javascripts/vendor/foundation/foundation.tooltips.js +195 -0
  51. data/lib/helios/frontend/javascripts/vendor/foundation/foundation.topbar.js +208 -0
  52. data/lib/helios/frontend/javascripts/vendor/jquery.js +9597 -0
  53. data/lib/helios/frontend/javascripts/vendor/jquery/jquery.fileupload-ui.js +807 -0
  54. data/lib/helios/frontend/javascripts/vendor/jquery/jquery.fileupload.js +1201 -0
  55. data/lib/helios/frontend/javascripts/vendor/jquery/jquery.ui.widget.js +530 -0
  56. data/lib/helios/frontend/javascripts/vendor/linkheaders.js +117 -0
  57. data/lib/helios/frontend/javascripts/vendor/underscore.js +1227 -0
  58. data/lib/helios/frontend/stylesheets/_codemirror.sass +219 -0
  59. data/lib/helios/frontend/stylesheets/_fonts.sass +80 -0
  60. data/lib/helios/frontend/stylesheets/_iphone.sass +141 -0
  61. data/lib/helios/frontend/stylesheets/_settings.scss +989 -0
  62. data/lib/helios/frontend/stylesheets/screen.sass +187 -0
  63. data/lib/helios/frontend/templates/data/entities.jst.tpl +11 -0
  64. data/lib/helios/frontend/templates/in-app-purchase/receipts.jst.tpl +11 -0
  65. data/lib/helios/frontend/templates/navigation.jst.tpl +31 -0
  66. data/lib/helios/frontend/templates/newsstand/issues.jst.tpl +16 -0
  67. data/lib/helios/frontend/templates/newsstand/new.jst.tpl +28 -0
  68. data/lib/helios/frontend/templates/passbook/passes.jst.tpl +11 -0
  69. data/lib/helios/frontend/templates/push-notification/compose.jst.tpl +70 -0
  70. data/lib/helios/frontend/templates/push-notification/devices.jst.tpl +17 -0
  71. data/lib/helios/frontend/views/index.haml +22 -0
  72. data/lib/helios/templates/.env.erb +1 -0
  73. data/lib/helios/templates/.gitignore +3 -0
  74. data/lib/helios/templates/Gemfile.erb +10 -0
  75. data/lib/helios/templates/Procfile.erb +1 -0
  76. data/lib/helios/templates/README.md.erb +4 -0
  77. data/lib/helios/templates/config.ru.erb +11 -0
  78. data/lib/helios/version.rb +3 -0
  79. metadata +475 -0
@@ -0,0 +1,10 @@
1
+ require "bundler"
2
+ Bundler.setup
3
+
4
+ gemspec = eval(File.read("helios.gemspec"))
5
+
6
+ task :build => "#{gemspec.full_name}.gem"
7
+
8
+ file "#{gemspec.full_name}.gem" => gemspec.files + ["helios.gemspec"] do
9
+ system "gem build helios.gemspec"
10
+ end
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'commander/import'
4
+ $:.push File.expand_path("../../lib", __FILE__)
5
+
6
+ require 'helios/version'
7
+
8
+ HighLine.track_eof = false # Fix for built-in Ruby
9
+ Signal.trap("INT") {} # Suppress backtrace when exiting command
10
+
11
+ program :version, Helios::VERSION
12
+ program :description, 'A command-line interface for building mobile infrastructures'
13
+
14
+ program :help, 'Author', 'Mattt Thompson <m@mattt.me>'
15
+ program :help, 'Website', 'https://helios.io'
16
+ program :help_formatter, :compact
17
+
18
+ default_command :help
19
+
20
+ require 'helios/commands'
@@ -0,0 +1,47 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "helios/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "helios_aim"
7
+ s.authors = ["Mattt Thompson"]
8
+ s.email = "mattt@heroku.com"
9
+ s.license = "MIT"
10
+ s.homepage = "http://helios.io"
11
+ s.version = Helios::VERSION
12
+ s.platform = Gem::Platform::RUBY
13
+ s.summary = "An extensible open-source mobile backend framework"
14
+ s.description = "Helios is an open-source framework that provides essential backend services for iOS apps, from data synchronization and user accounts to push notifications, in-app purchases, and passbook integration. It allows developers to get a client-server app up-and-running in just a few minutes, and seamlessly incorporate functionality as necessary."
15
+
16
+ s.add_dependency "commander", "~> 4.1"
17
+ s.add_dependency "foreman", "~> 0.63"
18
+ s.add_dependency "rack-contrib", "~> 1.1"
19
+ s.add_dependency "rack-push-notification", "~> 0.4"
20
+ s.add_dependency "rack-in-app-purchase", "~> 0.1"
21
+ s.add_dependency "rack-passbook", "~> 0.1"
22
+ s.add_dependency "rack-newsstand", "~> 0.1"
23
+ s.add_dependency "rack-scaffold_aim"
24
+ s.add_dependency "core_data"
25
+ s.add_dependency "json", "~> 1.7"
26
+ s.add_dependency "coffee-script", "~> 2.2"
27
+ s.add_dependency "sinatra", "~> 1.3"
28
+ s.add_dependency "sinatra-contrib", "~> 1.3"
29
+ s.add_dependency "sinatra-assetpack", "~> 0.2.2"
30
+ s.add_dependency "sinatra-backbone", "~> 0.1.1"
31
+ s.add_dependency "sinatra-param", "~> 0.1"
32
+ s.add_dependency "sinatra-support", "~> 1.2"
33
+ s.add_dependency "haml", ">= 3.1"
34
+ s.add_dependency "compass", "~> 0.12"
35
+ s.add_dependency "zurb-foundation", "4.1.2"
36
+ s.add_dependency "rails-database-url", "~> 1.0"
37
+ s.add_dependency "fog", "~> 1.10"
38
+ s.add_dependency "houston", "~> 0.2"
39
+
40
+ s.add_development_dependency "rake"
41
+ s.add_development_dependency "rspec"
42
+
43
+ s.files = Dir["./**/*"].reject{|file| file =~ /\.\/(bin|example|log|pkg|script|spec|test|vendor)/} + Dir.glob("./lib/helios/templates/*", File::FNM_DOTMATCH)
44
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
45
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
46
+ s.require_paths = ["lib"]
47
+ end
@@ -0,0 +1,27 @@
1
+ require 'rack'
2
+
3
+ module Helios
4
+ class Application
5
+ def initialize(app = nil, options = {}, &block)
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']
11
+
12
+ run Helios::Frontend.new
13
+ end
14
+
15
+ run Helios::Backend.new(&block)
16
+ end
17
+ end
18
+
19
+ def call(env)
20
+ @app.call(env)
21
+ end
22
+ end
23
+ end
24
+
25
+ require 'helios/backend'
26
+ require 'helios/frontend'
27
+ require 'helios/version'
@@ -0,0 +1,64 @@
1
+ require 'rack'
2
+
3
+ module Helios
4
+ class Backend < Rack::Builder
5
+ DEFAULT_PATHS = {
6
+ data: '/'
7
+ }
8
+
9
+ require 'rails-database-url' if const_defined?(:Rails)
10
+
11
+ def initialize(*args, &block)
12
+ raise ArgumentError, "Missing block" unless block_given?
13
+ super(&nil)
14
+
15
+ @services = {}
16
+
17
+ instance_eval(&block)
18
+ end
19
+
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")}, []]
29
+ end
30
+
31
+ private
32
+
33
+ def service(identifier, options = {}, &block)
34
+ if identifier.is_a?(Class)
35
+ middleware = identifier
36
+ else
37
+ begin
38
+ middleware = Helios::Backend.const_get(constantize(identifier))
39
+ rescue NameError
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})."
41
+ end
42
+ end
43
+
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
50
+
51
+ @services[path] = middleware
52
+ end
53
+
54
+ def constantize(identifier)
55
+ identifier.to_s.split(/([[:alpha:]]*)/).select{|c| /[[:alpha:]]/ === c}.map(&:capitalize).join("")
56
+ end
57
+ end
58
+ end
59
+
60
+ require 'helios/backend/data'
61
+ require 'helios/backend/in-app-purchase'
62
+ require 'helios/backend/passbook'
63
+ require 'helios/backend/push-notification'
64
+ require 'helios/backend/newsstand'
@@ -0,0 +1,40 @@
1
+ require 'core_data'
2
+ require 'sequel'
3
+
4
+ require 'rack/scaffold_aim'
5
+
6
+ require 'sinatra/base'
7
+ require 'sinatra/param'
8
+
9
+
10
+ class Helios::Backend::Data < Sinatra::Base
11
+ helpers Sinatra::Param
12
+
13
+ def initialize(app, options = {})
14
+ super(Rack::Scaffold.new(options))
15
+
16
+ @model = CoreData::DataModel.new(options[:model]) rescue nil
17
+ end
18
+
19
+ before do
20
+ content_type :json
21
+ end
22
+
23
+ options '/resources' do
24
+
25
+ links = []
26
+ @model.entities.each do |entity|
27
+ links << %{</#{entity.name.downcase.pluralize}>; rel="resource"}
28
+ end
29
+
30
+ response['Link'] = links.join("\n")
31
+
32
+ @model.entities.collect{ |entity|
33
+ {
34
+ name: entity.name,
35
+ url: "/#{entity.name.downcase.pluralize}",
36
+ attributes: Hash[entity.attributes.collect{|attribute| [attribute.name, attribute.type]}]
37
+ }
38
+ }.to_json
39
+ end
40
+ end
@@ -0,0 +1,43 @@
1
+ require 'rack/in-app-purchase'
2
+
3
+ require 'sinatra/base'
4
+ require 'sinatra/param'
5
+
6
+ class Helios::Backend::InAppPurchase < Sinatra::Base
7
+ helpers Sinatra::Param
8
+
9
+ def initialize(app, options = {}, &block)
10
+ super(Rack::InAppPurchase.new)
11
+ end
12
+
13
+ before do
14
+ content_type :json
15
+ end
16
+
17
+ helpers Sinatra::Param
18
+
19
+ get '/receipts' do
20
+ param :q, String
21
+
22
+ receipts = Rack::InAppPurchase::Receipt.dataset
23
+ receipts = receipts.filter("tsv @@ to_tsquery('english', ?)", "#{params[:q]}:*") if params[:q] and not params[:q].empty?
24
+
25
+ if params[:page] or params[:per_page]
26
+ param :page, Integer, default: 1, min: 1
27
+ param :per_page, Integer, default: 100, in: (1..100)
28
+
29
+ {
30
+ receipts: receipts.limit(params[:per_page], (params[:page] - 1) * params[:per_page]).naked.all,
31
+ page: params[:page],
32
+ total: receipts.count
33
+ }.to_json
34
+ else
35
+ param :limit, Integer, default: 100, in: (1..100)
36
+ param :offset, Integer, default: 0, min: 0
37
+
38
+ {
39
+ receipts: receipts.limit(params[:limit], params[:offset]).naked.all
40
+ }.to_json
41
+ end
42
+ end
43
+ 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
@@ -0,0 +1,41 @@
1
+ require 'rack/passbook'
2
+
3
+ require 'sinatra/base'
4
+ require 'sinatra/param'
5
+
6
+ class Helios::Backend::Passbook < Sinatra::Base
7
+ helpers Sinatra::Param
8
+
9
+ def initialize(app, options = {}, &block)
10
+ super(Rack::Passbook.new)
11
+ end
12
+
13
+ before do
14
+ content_type :json
15
+ end
16
+
17
+ get '/passes' do
18
+ param :q, String
19
+
20
+ passes = Rack::Passbook::Pass.dataset
21
+ passes = passes.filter("tsv @@ to_tsquery('english', ?)", "#{params[:q]}:*") if params[:q] and not params[:q].empty?
22
+
23
+ if params[:page] or params[:per_page]
24
+ param :page, Integer, default: 1, min: 1
25
+ param :per_page, Integer, default: 100, in: (1..100)
26
+
27
+ {
28
+ passes: passes.limit(params[:per_page], (params[:page] - 1) * params[:per_page]).naked.all,
29
+ page: params[:page],
30
+ total: passes.count
31
+ }.to_json
32
+ else
33
+ param :limit, Integer, default: 100, in: (1..100)
34
+ param :offset, Integer, default: 0, min: 0
35
+
36
+ {
37
+ passes: passes.limit(params[:limit], params[:offset]).naked.all
38
+ }.to_json
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,110 @@
1
+ require 'rack/push-notification'
2
+
3
+ require 'sinatra/base'
4
+ require 'sinatra/param'
5
+
6
+ require 'houston'
7
+
8
+ class Helios::Backend::PushNotification < Sinatra::Base
9
+ helpers Sinatra::Param
10
+ attr_reader :apn_certificate, :apn_environment
11
+
12
+ def initialize(app, options = {}, &block)
13
+ super(Rack::PushNotification.new)
14
+
15
+ @apn_certificate = options[:apn_certificate] || ENV['APN_CERTIFICATE']
16
+ @apn_environment = options[:apn_environment] || ENV['APN_ENVIRONMENT']
17
+ end
18
+
19
+ before do
20
+ content_type :json
21
+ end
22
+
23
+ get '/devices/?' do
24
+ param :q, String
25
+
26
+ devices = ::Rack::PushNotification::Device.dataset
27
+ devices = devices.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
+ devices: devices.limit(params[:per_page], (params[:page] - 1) * params[:per_page]),
35
+ page: params[:page],
36
+ total: devices.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
+ devices: devices.limit(params[:limit], params[:offset])
44
+ }.to_json
45
+ end
46
+ end
47
+
48
+ get '/devices/:token/?' do
49
+ record = ::Rack::PushNotification::Device.find(token: params[:token])
50
+
51
+ if record
52
+ {device: record}.to_json
53
+ else
54
+ status 404
55
+ end
56
+ end
57
+
58
+ head '/message' do
59
+ status 503 and return unless client
60
+
61
+ status 204
62
+ end
63
+
64
+ post '/message' do
65
+ status 503 and return unless client
66
+
67
+ param :payload, String, empty: false
68
+ param :tokens, Array, empty: false
69
+
70
+ tokens = params[:tokens] || ::Rack::PushNotification::Device.all.collect(&:token)
71
+
72
+ options = JSON.parse(params[:payload])
73
+ options[:alert] = options["aps"]["alert"]
74
+ options[:badge] = options["aps"]["badge"]
75
+ options[:sound] = options["aps"]["sound"]
76
+ options.delete("aps")
77
+
78
+ begin
79
+ notifications = tokens.collect{|token| Houston::Notification.new(options.update({device: token}))}
80
+ client.push(*notifications)
81
+
82
+ status 204
83
+ rescue => error
84
+ status 500
85
+
86
+ {error: error}.to_json
87
+ end
88
+ end
89
+
90
+ private
91
+
92
+ def client
93
+ begin
94
+ return nil unless apn_certificate and ::File.exist?(apn_certificate)
95
+
96
+ client = case apn_environment.to_sym
97
+ when :development
98
+ Houston::Client.development
99
+ when :production
100
+ Houston::Client.production
101
+ end
102
+
103
+ client.certificate = ::File.read(apn_certificate)
104
+
105
+ return client
106
+ rescue
107
+ return nil
108
+ end
109
+ end
110
+ end