Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial attempt at adding support for Firefox OS and JSON web app manifests #3

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# http://editorconfig.org
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.DS_Store
*.sw[po]
pkg/*
*.gem
.bundle
Expand Down
15 changes: 0 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@

## Usage

$ confetti generate android_manifest

or, from Ruby

require "confetti"
c_whatever = Confetti::Config.new "/some/dir/config.xml"
c_whatever.write_android_manifest "/some/dir/AndroidManifest.xml"
Expand All @@ -30,13 +26,6 @@ Supported outputs right now: `android_manifest`, `android_strings`, `webos_appin

Let's say you want write a `nintendo_ds_config` generator:

* create a feature file, like `features/nintendo.feature`
* specify all the files to generate for your platform
* specify the name each file will go under
* `nintendo_ds_config`
* and the default filename
* `NintendoDSConfig.xml`
* run `rake features` to verify that it fails
* add a sample configuration file to `spec/fixtures`
* the filename is based on the descriptive name
* `nintendo_ds_config_expected.xml`
Expand Down Expand Up @@ -66,7 +55,3 @@ Let's say you want write a `nintendo_ds_config` generator:
* add your platform to the `generate_and_write` call
* `generate_and_write ... :nintendo_ds_config`
* run `rake spec` again
* build and install the gem, or whatever, so the cucumber tests work
* run `feature/nintendo.feature`
* it's all green!
* you're the man now dog
1 change: 1 addition & 0 deletions lib/confetti.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
require 'confetti/templates/ios_info'
require 'confetti/templates/ios_remote_plist'
require 'confetti/templates/windows_phone8_manifest'
require 'confetti/templates/firefoxos_manifest'

require 'confetti/template_helper'

Expand Down
3 changes: 2 additions & 1 deletion lib/confetti/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ class Config
:url_scheme_set, :platform_set

generate_and_write :android_manifest, :android_strings, :ios_info,
:ios_remote_plist, :windows_phone8_manifest
:ios_remote_plist, :windows_phone8_manifest,
:firefoxos_manifest

# handle bad generate/write calls
def method_missing(method_name, *args)
Expand Down
163 changes: 163 additions & 0 deletions lib/confetti/templates/firefoxos_manifest.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
module Confetti
module Template
class FirefoxosManifest < Base

# See: https://developer.mozilla.org/en-US/Apps/Build/Manifest

REQUIRED_FIELDS = ['name', 'description', 'launch_path', 'icons',
'developer', 'default_locale', 'type']

OPTIONAL_FIELDS = ['version', 'fullscreen', 'orientation', 'permissions']

ALL_FIELDS = REQUIRED_FIELDS + OPTIONAL_FIELDS

# Unhandled / unmappable fields:
#
# activities
# appcache_path
# chrome
# csp
# installs_allowed_from
# locales
# messages
# moz-firefox-accounts
# origin
# precompile
# redirects
# role

ORIENTATIONS_MAP = {
:default => nil,
:landscape => "landscape",
:portrait => "portrait"
}

GAP_PERMISSIONS_MAP = {
"camera" => %w{camera},
"notification" => %w{desktop-notification},
"geolocation" => %w{geolocation},
"media" => %w{audio-capture
camera
video-capture},
"contacts" => %w{contacts},
"file" => %w{storage
device-storage:videos
device-storage:sdcard
device-storage:pictures
device-storage:music},
"network" => %w{mobilenetwork
tcp-socket
systemXHR},
"battery" => %w{}
}

# TODO: Need a way to more granularly control access?
PERMISSIONS_ACCESS_MAP = {
"contacts" => 'readwrite',
"storage" => 'readwrite',
"device-storage:videos" => 'readwrite',
"device-storage:sdcard" => 'readwrite',
"device-storage:pictures" => 'readwrite',
"device-storage:music" => 'readwrite'
}

def output_filename
"manifest.webapp"
end

def name
@config.name.name
end

def description
@config.description
end

def version
@config.version_string || '0.0.1'
end

def launch_path
"index.html"
end

def icons
out = {}
@config.icon_set.each do |icon|
out[icon.width] = icon.src
end
out
end

def developer
out = {
"name" => @config.author.name
}
out['url'] = @config.author.href if @config.author.href
out
end

def type
'privileged'
end

def default_locale
'en'
end

def fullscreen
true
end

def orientation
ORIENTATIONS_MAP[@config.orientation]
end

def permissions
names = translate_feature GAP_PERMISSIONS_MAP
if names.empty?
out = nil
else
out = {}
names.each do |name|
out[name] = {
# TODO: Need to somehow derive a better description?
"description" => "Required feature"
}
out[name]['access'] = PERMISSIONS_ACCESS_MAP[name] if PERMISSIONS_ACCESS_MAP[name]
end
end
out
end

def translate_feature map
features = []
phonegap_api = /http\:\/\/api.phonegap.com\/1[.]0\/(\w+)/
feature_names = @config.feature_set.map { |f| f.name }
feature_names = feature_names - [nil]
feature_names.sort

feature_names.each do |f|
feature_name = f.match(phonegap_api)[1] if f.match(phonegap_api)
associated_features = map[feature_name]

features.concat(associated_features) if associated_features
end

features.sort!
features
end

# HACK: Override Mustache rendering because JSON is easier & more robust
def render
out = {}
ALL_FIELDS.each do |name|
val = send(name)
out[name] = val if val != nil
end
JSON.pretty_generate out
end

end
end
end
15 changes: 15 additions & 0 deletions spec/fixtures/firefoxos/firefoxos_manifest_spec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "Awesome App",
"description": "This is an awesome app",
"launch_path": "index.html",
"version": "0.0.1",
"icons": {
"128": "icon.png"
},
"developer": {
"name": "Awesome Developer"
},
"default_locale": "en",
"type": "privileged",
"fullscreen": true
}
118 changes: 118 additions & 0 deletions spec/templates/firefoxos_manifest_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
require 'spec_helper'
require 'json'

describe Confetti::Template::FirefoxosManifest do
include HelpfulPaths

before :all do
@template_class = Confetti::Template::FirefoxosManifest
end

it "should inherit from the base template" do
@template_class.superclass.should be Confetti::Template::Base
end

it "should have the template_file \"firefoxos.mustache\" in the confetti/templates dir" do
@template_class.template_file.should == "#{ templates_dir }/firefoxos_manifest.mustache"
end

describe "default values" do
it "should define output filename as \"manifest.webapp\"" do
@template_class.new.output_filename.should == "manifest.webapp"
end
end

describe "when passed a config object" do
before do
@config = Confetti::Config.new
@config.name.name = "Awesome App"
@config.author.name = "Awesome Developer"
@config.description = "This is an awesome app"

icon = Confetti::Config::Image.new('icon.png', '128', '128', {}, 0)
@config.icon_set << icon
end

it "should accept the config object" do
lambda {
@template_class.new(@config)
}.should_not raise_error
end

describe "templated attributes" do
before do
@template = @template_class.new(@config)
end

it "should set name correctly" do
@template.name.should == "Awesome App"
end

it "should set description correctly" do
@template.description.should == "This is an awesome app"
end

it "should use the default version" do
@template.version.should == "0.0.1"
end

it "should set developer info correctly" do
@template.developer['name'] == @config.author.name
@template.developer.should_not include 'url'
end

it "should render the correct web app manifest" do
expected_json = File.read("#{ fixture_dir }/firefoxos/firefoxos_manifest_spec.json")
expected = JSON.parse(expected_json)
result = JSON.parse(@template.render)
result.should == expected
end
end

end

it "should populate the manifest with all the icons" do
@config = Confetti::Config.new("#{fixture_dir}/config-icons.xml")
@template = @template_class.new(@config)
result = JSON.parse(@template.render)
result['icons'].should == {
"100"=>"smallicon.png",
"200"=>"bigicon.png"
}
end

it "should handle orientation preference" do
@config = Confetti::Config.new("#{fixture_dir}/config_with_orientation.xml")
@template = @template_class.new(@config)

JSON.parse(@template.render)['orientation'].should == 'landscape'

@config.preference_set.clear()
JSON.parse(@template.render).should_not include 'orientation'

@config.preference_set << Confetti::Config::Preference.new(
'orientation', 'portrait', false)
JSON.parse(@template.render)['orientation'].should == 'portrait'
end

it "should handle permissions" do
@config = Confetti::Config.new("#{fixture_dir}/config-features.xml")
@config.feature_set << Confetti::Config::Feature.new(
'http://api.phonegap.com/1.0/contacts', false)
@template = @template_class.new(@config)

result = JSON.parse(@template.render)
result['permissions'].should == {
"desktop-notification" => { "description" => "Required feature" },
"geolocation" => { "description" => "Required feature" },
"mobilenetwork" => { "description" => "Required feature" },
"systemXHR" => { "description" => "Required feature" },
"tcp-socket" => { "description" => "Required feature" },
"contacts" => {
"description" => "Required feature",
"access" => "readwrite"
}
}
end

end