wod 0.0.1

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.
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
+