wod 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in wod.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,47 @@
1
+ The Wizard Of Dev center interacts with the Apple Dev Center web pages so you don't have to!
2
+
3
+ Install
4
+ ---
5
+
6
+ $ gem install wod
7
+
8
+ Usage
9
+ ---
10
+ $ wod help
11
+
12
+ === General Commands
13
+
14
+ help # show this usage
15
+ version # show the gem version
16
+
17
+ login # log in with your apple credentials
18
+ logout # clear local authentication credentials
19
+
20
+ devices # list your registered devices
21
+ devices:add <name> <udid> # add a new device
22
+ devices:remove <name> # remove device (Still counts against device limit)
23
+
24
+ Examples
25
+ ---
26
+
27
+ List Devices:
28
+
29
+ $ wod devices
30
+ Steve Jobs iPad 3 | 554f3fg54bc953547ry7a6bd62c678c11e912345
31
+ Jon Ive's iPhone 5 | 2d84d56ceg52c49379537413d3b9865ae2b12345
32
+
33
+ Add Device:
34
+
35
+ $ wod devices:add "Dave's iPod Touch" 2d84d56ceg52c49379537413d3b9865ae2b12345
36
+
37
+ Remove Device:
38
+
39
+ $ wod devices:remove "Jon Ive's iPhone 5"
40
+
41
+
42
+ Roadmap
43
+ ---
44
+
45
+ More features to come as I need them or as other people fork and add em ;)
46
+
47
+ Proudly brought to you by the wizard of identity crises (whatupdave/snappycode/wizard of id)
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/bin/wod ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join File.dirname(__FILE__), '../lib'
4
+
5
+ require "wod"
6
+
7
+ args = ARGV.dup
8
+ ARGV.clear
9
+ command = args.shift.strip rescue 'help'
10
+
11
+ Wod::Command.run command, args
data/lib/wod.rb ADDED
@@ -0,0 +1,6 @@
1
+ module Wod
2
+ class InvalidCredentials < RuntimeError; end
3
+ end
4
+
5
+ require 'wod/client'
6
+ require 'wod/command'
data/lib/wod/client.rb ADDED
@@ -0,0 +1,81 @@
1
+ require 'mechanize'
2
+ require 'wod/helpers'
3
+ require 'wod/version'
4
+
5
+ module Wod
6
+ class DevCenterPage
7
+ attr_reader :page
8
+
9
+ def initialize page
10
+ @page = page
11
+ end
12
+
13
+ def logged_in?
14
+ (page.title =~ /sign in/i) == nil && (page.search("span").find {|s| s.text == "Log in"}) == nil
15
+ end
16
+
17
+ def search arg
18
+ @page.search arg
19
+ end
20
+
21
+ def form arg
22
+ @page.form arg
23
+ end
24
+ end
25
+
26
+ class Client
27
+ include Wod::Helpers
28
+
29
+ attr_reader :name
30
+
31
+ def initialize username, password
32
+ @username = username
33
+ @password = password
34
+ end
35
+
36
+ def cookies_file
37
+ "#{home_directory}/.wod/cookie_jar"
38
+ end
39
+
40
+ def create_agent
41
+ agent = Mechanize.new
42
+ agent.cookie_jar.load cookies_file if File.exists?(cookies_file)
43
+ agent
44
+ end
45
+
46
+ def agent
47
+ @agent ||= create_agent
48
+ end
49
+
50
+ def login_and_reopen url
51
+ page = DevCenterPage.new agent.get("https://developer.apple.com/devcenter/ios/index.action")
52
+
53
+ unless page.logged_in?
54
+ puts "Creating session"
55
+ login_page = page.page.links.find { |l| l.text == 'Log in'}.click
56
+
57
+ f = login_page.form("appleConnectForm")
58
+ f.theAccountName = @username
59
+ f.theAccountPW = @password
60
+ f.submit
61
+ end
62
+
63
+ page = DevCenterPage.new agent.get url
64
+ raise InvalidCredentials unless page.logged_in?
65
+ agent.cookie_jar.save_as cookies_file
66
+ page
67
+ end
68
+
69
+ def get url
70
+ page = DevCenterPage.new agent.get(url)
71
+ page = login_and_reopen(url) unless page.logged_in?
72
+ page
73
+ end
74
+
75
+ def logged_in?
76
+ page = get "https://developer.apple.com/devcenter/ios/index.action"
77
+ page.logged_in?
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,59 @@
1
+ require 'wod/helpers'
2
+ require 'wod/commands/base'
3
+
4
+ Dir["#{File.dirname(__FILE__)}/commands/*.rb"].each { |c| require c }
5
+
6
+ module Wod
7
+ module Command
8
+ class InvalidCommand < RuntimeError; end
9
+ class CommandFailed < RuntimeError; end
10
+
11
+ extend Wod::Helpers
12
+
13
+ def self.run(command, args, retries=0)
14
+ begin
15
+ run_internal 'auth:reauthorize', args.dup if retries > 0
16
+ run_internal command, args.dup
17
+ rescue InvalidCommand
18
+ error "Unknown command. Run 'wod help' for usage information."
19
+ rescue Wod::InvalidCredentials
20
+ if retries < 3
21
+ STDERR.puts "Authentication failure"
22
+ run command, args, retries + 1
23
+ else
24
+ error "Authentication failure"
25
+ end
26
+ end
27
+
28
+ end
29
+
30
+ def self.run_internal(command, args, wod=nil)
31
+ klass, method = parse command
32
+ runner = klass.new args, wod
33
+ raise InvalidCommand unless runner.respond_to?(method)
34
+ runner.send method
35
+ end
36
+
37
+ def self.parse(command)
38
+ parts = command.split(':')
39
+ case parts.size
40
+ when 1
41
+ begin
42
+ return eval("Wod::Command::#{command.capitalize}"), :index
43
+ rescue NameError, NoMethodError
44
+ return Wod::Command::App, command.to_sym
45
+ end
46
+ else
47
+ begin
48
+ const = Wod::Command
49
+ command = parts.pop
50
+ parts.each { |part| const = const.const_get(part.capitalize) }
51
+ return const, command.to_sym
52
+ rescue NameError
53
+ raise InvalidCommand
54
+ end
55
+ end
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,15 @@
1
+ require 'readline'
2
+
3
+ module Wod::Command
4
+ class App < Base
5
+ def login
6
+ Wod::Command.run_internal "auth:reauthorize", args.dup
7
+ end
8
+
9
+ def logout
10
+ Wod::Command.run_internal "auth:delete_credentials", args.dup
11
+ puts "Local credentials cleared."
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,113 @@
1
+ require 'readline'
2
+
3
+ module Wod::Command
4
+ class Auth < Base
5
+ attr_accessor :credentials
6
+
7
+ def client
8
+ @client = Wod::Client.new(user, password)
9
+ end
10
+
11
+ # just a stub; will raise if not authenticated
12
+ def check
13
+ client.logged_in?
14
+ end
15
+
16
+ def reauthorize
17
+ @credentials = ask_for_and_save_credentials
18
+ end
19
+
20
+ def user
21
+ get_credentials
22
+ @credentials[0]
23
+ end
24
+
25
+ def password
26
+ get_credentials
27
+ @credentials[1]
28
+ end
29
+
30
+ def credentials_file
31
+ "#{home_directory}/.wod/credentials"
32
+ end
33
+
34
+ def get_credentials
35
+ return if @credentials
36
+ unless @credentials = read_credentials
37
+ ask_for_and_save_credentials
38
+ end
39
+ @credentials
40
+ end
41
+
42
+ def read_credentials
43
+ File.exists?(credentials_file) and File.read(credentials_file).split("\n")
44
+ end
45
+
46
+ def echo_off
47
+ system "stty -echo"
48
+ end
49
+
50
+ def echo_on
51
+ system "stty echo"
52
+ end
53
+
54
+ def ask_for_credentials
55
+ puts "Enter your apple credentials"
56
+
57
+ print "Apple ID: "
58
+ user = ask
59
+
60
+ print "Password: "
61
+ password = ask_for_password
62
+
63
+ [user, password]
64
+ end
65
+
66
+ def ask_for_password
67
+ echo_off
68
+ password = ask
69
+ puts
70
+ echo_on
71
+ return password
72
+ end
73
+
74
+ def ask_for_and_save_credentials
75
+ begin
76
+ @credentials = ask_for_credentials
77
+ write_credentials
78
+ check
79
+ rescue ::Wod::InvalidCredentials
80
+ delete_credentials
81
+ @client = nil
82
+ @credentials = nil
83
+ puts "Authentication failed."
84
+ return if retry_login?
85
+ exit 1
86
+ end
87
+ end
88
+
89
+ def retry_login?
90
+ @login_attempts ||= 0
91
+ @login_attempts += 1
92
+ @login_attempts < 3
93
+ end
94
+
95
+ def write_credentials
96
+ FileUtils.mkdir_p(File.dirname(credentials_file))
97
+ f = File.open(credentials_file, 'w')
98
+ f.chmod(0600)
99
+ f.puts self.credentials
100
+ f.close
101
+ set_credentials_permissions
102
+ end
103
+
104
+ def set_credentials_permissions
105
+ FileUtils.chmod 0700, File.dirname(credentials_file)
106
+ FileUtils.chmod 0600, credentials_file
107
+ end
108
+
109
+ def delete_credentials
110
+ FileUtils.rm_rf "#{home_directory}/.wod/"
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,18 @@
1
+ require 'fileutils'
2
+
3
+ module Wod::Command
4
+ class Base
5
+ include Wod::Helpers
6
+
7
+ attr_accessor :args
8
+
9
+ def initialize(args, wod=nil)
10
+ @args = args
11
+ @wod = wod
12
+ end
13
+
14
+ def wod
15
+ @wod ||= Wod::Command.run_internal('auth:client', args)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,50 @@
1
+
2
+ module Wod::Command
3
+ class Devices < Base
4
+ def index
5
+ page = wod.get "https://developer.apple.com/ios/manage/devices/index.action"
6
+
7
+ names = page.search("td.name span").map(&:text)
8
+ udids = page.search("td.id").map(&:text)
9
+
10
+ devices_left = page.search(".devicesannounce strong").first.text
11
+ devices = names.map.with_index{|name, i| {:name => name, :udid => udids[i] } }
12
+
13
+ display_formatted devices, [:name, :udid]
14
+ puts
15
+ puts "#{devices.size} devices registered. #{devices_left}"
16
+ end
17
+
18
+ def add
19
+ name = args.shift
20
+ udid = args.shift
21
+
22
+ page = wod.get "https://developer.apple.com/ios/manage/devices/add.action"
23
+
24
+ form = page.form "add"
25
+ form["deviceNameList[0]"] = name
26
+ form["deviceNumberList[0]"] = udid
27
+
28
+ form.submit
29
+ end
30
+
31
+ def remove
32
+ name = args.shift
33
+
34
+ page = wod.get "http://developer.apple.com/ios/manage/devices/index.action"
35
+
36
+ device_span = page.search("span:contains('#{name}')")
37
+ if device_span.empty?
38
+ error "Device not found"
39
+ end
40
+
41
+ tr = device_span.first.parent.parent
42
+ row_identifier = tr.search("input[name='__checkbox_selectedValues']").first[:value]
43
+
44
+ form = page.form "removeDevice"
45
+ checkbox = form.checkboxes.find {|c| c[:value] == row_identifier}
46
+ checkbox.check
47
+ form.submit
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,76 @@
1
+ module Wod::Command
2
+ class Help < Base
3
+ class HelpGroup < Array
4
+ attr_reader :title
5
+
6
+ def initialize(title)
7
+ @title = title
8
+ end
9
+
10
+ def command(name, description)
11
+ self << [name, description]
12
+ end
13
+
14
+ def space
15
+ self << ['', '']
16
+ end
17
+ end
18
+
19
+ def self.groups
20
+ @groups ||= []
21
+ end
22
+
23
+ def self.group(title, &block)
24
+ groups << begin
25
+ group = HelpGroup.new(title)
26
+ yield group
27
+ group
28
+ end
29
+ end
30
+
31
+ def self.create_default_groups!
32
+ return if @defaults_created
33
+ @defaults_created = true
34
+ group 'General Commands' do |group|
35
+ group.command 'help', 'show this usage'
36
+ group.command 'version', 'show the gem version'
37
+ group.space
38
+ group.command 'login', 'log in with your apple credentials'
39
+ group.command 'logout', 'clear local authentication credentials'
40
+ group.space
41
+ group.command 'devices', 'list your registered devices'
42
+ group.command 'devices:add <name> <udid>', 'add a new device'
43
+ group.command 'devices:remove <name>', 'remove device (Still counts against device limit)'
44
+ end
45
+ end
46
+
47
+ def index
48
+ puts usage
49
+ end
50
+
51
+ def usage
52
+ longest_command_length = self.class.groups.map do |group|
53
+ group.map { |g| g.first.length }
54
+ end.flatten.max
55
+
56
+ self.class.groups.inject(StringIO.new) do |output, group|
57
+ output.puts "=== %s" % group.title
58
+ output.puts
59
+
60
+ group.each do |command, description|
61
+ if command.empty?
62
+ output.puts
63
+ else
64
+ output.puts "%-*s # %s" % [longest_command_length, command, description]
65
+ end
66
+ end
67
+
68
+ output.puts
69
+ output
70
+ end.string
71
+ end
72
+
73
+ end
74
+ end
75
+
76
+ Wod::Command::Help.create_default_groups!
@@ -0,0 +1,7 @@
1
+ module Wod::Command
2
+ class Version < Base
3
+ def index
4
+ puts Wod::Client.gem_version_string
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,29 @@
1
+ module Wod
2
+ module Helpers
3
+ def home_directory
4
+ ENV['HOME']
5
+ end
6
+
7
+ def error(msg)
8
+ STDERR.puts(msg)
9
+ exit 1
10
+ end
11
+
12
+ def ask
13
+ gets.strip
14
+ end
15
+
16
+ def display_formatted hashes, columns
17
+ column_lengths = columns.map do |c|
18
+ hashes.map { |h| h[c].length }.max
19
+ end
20
+ sorted = hashes.sort_by {|h| h[columns.first] }
21
+
22
+ sorted.each.with_index do |h, i|
23
+ puts " " + columns.map.with_index{ |c, i| h[c].ljust(column_lengths[i]) }.join(" | ")
24
+ end
25
+ end
26
+
27
+
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module Wod
2
+ VERSION = "0.0.1"
3
+ end
data/wod.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "wod/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "wod"
7
+ s.version = Wod::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Dave Newman"]
10
+ s.email = ["dave@whatupdave.com"]
11
+ s.homepage = "http://github.com/snappycode/wod"
12
+ s.summary = %q{The Wizard Of Dev center}
13
+ s.description = %q{Command line tool for interacting with the Apple Dev Center}
14
+
15
+ s.rubyforge_project = "wod"
16
+
17
+ s.add_dependency "mechanize"
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
+ s.require_paths = ["lib"]
23
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wod
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Dave Newman
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-04-01 00:00:00 +11:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: mechanize
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: "0"
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ description: Command line tool for interacting with the Apple Dev Center
28
+ email:
29
+ - dave@whatupdave.com
30
+ executables:
31
+ - wod
32
+ extensions: []
33
+
34
+ extra_rdoc_files: []
35
+
36
+ files:
37
+ - .gitignore
38
+ - Gemfile
39
+ - README.md
40
+ - Rakefile
41
+ - bin/wod
42
+ - lib/wod.rb
43
+ - lib/wod/client.rb
44
+ - lib/wod/command.rb
45
+ - lib/wod/commands/app.rb
46
+ - lib/wod/commands/auth.rb
47
+ - lib/wod/commands/base.rb
48
+ - lib/wod/commands/devices.rb
49
+ - lib/wod/commands/help.rb
50
+ - lib/wod/commands/version.rb
51
+ - lib/wod/helpers.rb
52
+ - lib/wod/version.rb
53
+ - wod.gemspec
54
+ has_rdoc: true
55
+ homepage: http://github.com/snappycode/wod
56
+ licenses: []
57
+
58
+ post_install_message:
59
+ rdoc_options: []
60
+
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: "0"
75
+ requirements: []
76
+
77
+ rubyforge_project: wod
78
+ rubygems_version: 1.5.3
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: The Wizard Of Dev center
82
+ test_files: []
83
+