tamiyo 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bbf3ec46f9860183fa06e9c767262c3c214f5562
4
+ data.tar.gz: 955834fbde1e9d80fad20ebf9715a360683de6a4
5
+ SHA512:
6
+ metadata.gz: ea01d30d6f7c33ae1ab5ec49536a669515b61fa750f0d847ad0428f7295c1a4ec550596ff763c4a8a4dc2d60d35b54ecad819485ced4d96e9a214a4281536db9
7
+ data.tar.gz: 9606a0a8b306362f15c54d946ca416a1567071ed1e3c3618b65c85fd02fd5e9846a8f640b6429f9d54d5053cf596ae8fb201a87b6a84f026270821bb8aa08172
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ Gemfile.lock
2
+ /pkg
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENCE.txt ADDED
@@ -0,0 +1,22 @@
1
+
2
+ The MIT License (MIT)
3
+
4
+ Copyright (c) 2013 Roger Norling
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ task default: [:install]
data/Readme.md ADDED
@@ -0,0 +1,4 @@
1
+ tamiyo
2
+ ======
3
+
4
+ A utility that import cards from Wizard's Gatherer. Allows for incremental imports and selective export to Cockatrice's card database format.
data/bin/tamiyo ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'tamiyo'
data/lib/tamiyo.rb ADDED
@@ -0,0 +1,22 @@
1
+ require 'slop'
2
+
3
+ opts = Slop.new help: true do
4
+ banner "Usage: #{File.basename __FILE__} <command>"
5
+
6
+ command 'dummy-scrape' do
7
+ run do |opts, args|
8
+ require_relative 'tamiyo/dummy_scrape_chain'
9
+ Tamiyo::DummyScrapeChain.create.pump
10
+ end
11
+ end
12
+
13
+ command 'gatherer-scrape' do
14
+ run do |opts, args|
15
+ require_relative 'tamiyo/gatherer_scrape_chain'
16
+ Tamiyo::GathererScrapeChain.create(args.first || "Dragon's Maze").pump
17
+ end
18
+ end
19
+ end
20
+
21
+ opts.parse
22
+ puts opts if ARGV.empty?
@@ -0,0 +1,26 @@
1
+ require_relative 'value'
2
+
3
+ module Tamiyo
4
+ Card = Value.new(:name, :cost, :colors, :type, :text, :pt, :loyalty,
5
+ :table_row, :played_tapped, :picture_url) do
6
+ def with_split(new_text)
7
+ Card.new(name, cost, colors, type, "#{text}\n---\n#{new_text}",
8
+ pt, loyalty, table_row, played_tapped, picture_url)
9
+ end
10
+
11
+ def inspect
12
+ ["Name: #{name}",
13
+ "Cost: #{cost}",
14
+ "Color identity: #{colors.join}",
15
+ "Card type: #{type}",
16
+ 'Rules text:',
17
+ text,
18
+ pt && "P/T: #{pt}",
19
+ loyalty && "Loyalty: #{loyalty}",
20
+ "Row: #{table_row}",
21
+ "Played tapped: #{played_tapped}",
22
+ "Picture url: #{picture_url}"
23
+ ].compact.join "\n"
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,57 @@
1
+ require 'nokogiri'
2
+
3
+ module Tamiyo
4
+ class DummyScrape
5
+ def xml_rows
6
+ Nokogiri::HTML(<<TestXml).css 'tr'
7
+ <div class="textspoiler">
8
+ <table>
9
+ <tr>
10
+ <td>Name</td>
11
+ <td><a id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_cardEntries_ctl00_cardLink" class="nameLink" onclick="return CardLinkAction(event, this, 'SameWindow');" href="../Card/Details.aspx?multiverseid=369036">Advent of the Wurm</a></td></tr>
12
+ <tr id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_cardEntries_ctl00_costRow">
13
+ <td>Cost:</td>
14
+ <td>1GGW</td></tr>
15
+ <tr>
16
+ <td>Type:</td>
17
+ <td>Instant</td></tr>
18
+ <tr>
19
+ <td>Pow/Tgh:</td>
20
+ <td></td></tr>
21
+ <tr>
22
+ <td>Rules Text:</td>
23
+ <td>Put a 5/5 green Wurm creature token with trample onto the battlefield.</td></tr>
24
+ <tr>
25
+ <td>Set/Rarity:</td>
26
+ <td>Dragon's Maze Rare</td></tr>
27
+ <tr>
28
+ <td colspan="2"><br /></td></tr>
29
+ <tr>
30
+ <td>Name</td>
31
+ <td><a id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_cardEntries_ctl01_cardLink" class="nameLink" onclick="return CardLinkAction(event, this, 'SameWindow');" href="../Card/Details.aspx?multiverseid=368961">Ætherling</a></td></tr>
32
+ <tr id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_cardEntries_ctl01_costRow">
33
+ <td>Cost:</td>
34
+ <td>4UU</td></tr>
35
+ <tr>
36
+ <td>Type:</td>
37
+ <td>Creature — Shapeshifter</td></tr>
38
+ <tr>
39
+ <td>Pow/Tgh:</td>
40
+ <td>(4/5)</td></tr>
41
+ <tr>
42
+ <td>Rules Text:</td>
43
+ <td>{U}: Exile Ætherling. Return it to the battlefield under its owner's control at the beginning of the next end step.<br />
44
+ {U}: Ætherling can't be blocked this turn.<br />
45
+ {1}: Ætherling gets +1/-1 until end of turn.<br />
46
+ {1}: Ætherling gets -1/+1 until end of turn.</td></tr>
47
+ <tr>
48
+ <td>Set/Rarity:</td>
49
+ <td>Dragon's Maze Rare</td></tr>
50
+ <tr>
51
+ <td colspan="2"><br /></td></tr>
52
+ </table>
53
+ </div>
54
+ TestXml
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,11 @@
1
+ require_relative 'dummy_scrape'
2
+ require_relative 'scrape_parser'
3
+ require_relative 'puts_sink'
4
+
5
+ module Tamiyo
6
+ class DummyScrapeChain
7
+ def self.create
8
+ Process.chain DummyScrape.new, ScrapeParser.new, PutsSink.new
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,26 @@
1
+ require 'httpclient'
2
+ require 'nokogiri'
3
+
4
+ module Tamiyo
5
+ class GathererScrape
6
+ def initialize(set_name)
7
+ @set_name = set_name
8
+ end
9
+
10
+ def xml_rows
11
+ host = 'http://gatherer.wizards.com/'
12
+ path = 'Pages/Search/Default.aspx'
13
+ query = '?output=spoiler&method=text&action=advanced&set=["%s"]&special=true' % @set_name.tr(' ', '+')
14
+ url = URI.escape "#{host}#{path}#{query}"
15
+ response = HTTPClient.new.get url
16
+ html = Nokogiri::HTML ampersand_fixed(response.body)
17
+ html.css 'div.textspoiler tr'
18
+ end
19
+
20
+ private
21
+
22
+ def ampersand_fixed(text)
23
+ text.gsub /&(?![^;]{,4};)/, '&amp;'
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,11 @@
1
+ require_relative 'gatherer_scrape'
2
+ require_relative 'scrape_parser'
3
+ require_relative 'puts_sink'
4
+
5
+ module Tamiyo
6
+ class GathererScrapeChain
7
+ def self.create(set)
8
+ Process.chain GathererScrape.new(set), ScrapeParser.new, PutsSink.new
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ module Tamiyo
2
+ module Process
3
+ module Node
4
+ def affix(node)
5
+ @chain = node
6
+ end
7
+ end
8
+
9
+ def self.chain(*nodes)
10
+ nodes.inject {|p, n| n.affix p; n}
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ require_relative 'process'
2
+
3
+ module Tamiyo
4
+ class PutsSink
5
+ include Process::Node
6
+
7
+ def pump
8
+ puts @chain.pull.inspect
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,129 @@
1
+ require_relative 'process'
2
+ require_relative 'card'
3
+
4
+ module Tamiyo
5
+ class ScrapeParser
6
+ include Process::Node
7
+
8
+ def pull
9
+ parse_cards_from @chain.xml_rows
10
+ end
11
+
12
+ def parse_cards_from(xml)
13
+ xml.each_with_object({}) do |tr, collection|
14
+ tds = tr.css 'td'
15
+ if tds.length == 2
16
+ left, right = *tds
17
+ right_text = right.content.tr '—', '-'
18
+ case fold left.content
19
+ when 'Name'
20
+ href = right.at_css('a')['href']
21
+ @id = href.match(/multiverseid=(.*)/)[1]
22
+ @name = fold right_text
23
+ when 'Cost:'
24
+ @cost = fold right_text
25
+ when 'Type:'
26
+ @type = fold right_text
27
+ when 'Pow/Tgh:'
28
+ @pt = fold(right_text).tr '()', ''
29
+ @pt = nil if @pt.empty?
30
+ when 'Loyalty:'
31
+ @loyalty = right_text.strip.tr '()', ''
32
+ when 'Rules Text:'
33
+ @text = right_text.strip.each_line.map(&:strip).join "\n"
34
+ end
35
+ else
36
+ card = parse_card collection
37
+ collection[card.name] = card
38
+ @pt = @loyalty = nil
39
+ end
40
+ end.values
41
+ end
42
+
43
+ def parse_card(collection)
44
+ split_card = @name[match_split_card]
45
+ @name = beautify @name
46
+
47
+ if collection.include? @name
48
+ card = collection[@name]
49
+ if split_card && !card.text.include?(@text)
50
+ card.with_split @text
51
+ else
52
+ card
53
+ end
54
+ else
55
+ Card.with name: @name,
56
+ cost: @cost,
57
+ type: @type,
58
+ text: @text,
59
+ pt: @pt,
60
+ loyalty: @loyalty,
61
+ colors: colors,
62
+ table_row: table_row,
63
+ played_tapped: played_tapped,
64
+ picture_url: picture_url
65
+ end
66
+ end
67
+
68
+ def beautify(name)
69
+ name.gsub(match_split_card, '')
70
+ .gsub('XX', '')
71
+ .gsub('Æ', 'AE')
72
+ .tr('’', "'")
73
+ end
74
+
75
+ def colors
76
+ {'W' => ' is white.',
77
+ 'U' => ' is blue.',
78
+ 'B' => ' is black.',
79
+ 'R' => ' is red.',
80
+ 'G' => ' is green.'
81
+ }.each_with_object [] do |(color, identity), colors|
82
+ if @cost.include?(color) || @text.include?(@name + identity)
83
+ colors << color
84
+ end
85
+ end
86
+ end
87
+
88
+ def table_row
89
+ mana_source = @type.end_with?('Artifact') &&
90
+ @text.include?('{T}') && @text.include?('to your mana pool')
91
+ case main_type_based_on @type
92
+ when /Land/, mana_source
93
+ :first_row
94
+ when /Sorcery|Instant/
95
+ :stack
96
+ when /Creature/
97
+ :third_row
98
+ else
99
+ :second_row
100
+ end
101
+ end
102
+
103
+ def main_type_based_on(type)
104
+ # The last word before a dash or $
105
+ type.match(/(\w+)\s*(-|$)/)[1]
106
+ end
107
+
108
+ def played_tapped
109
+ @text.include?(@name + ' enters the battlefield tapped.')
110
+ end
111
+
112
+ def picture_url
113
+ host = 'http://gatherer.wizards.com/'
114
+ path = 'Handlers/Image.ashx'
115
+ query = '?multiverseid=%s&amp;type=card' % @id
116
+ "#{host}#{path}#{query}"
117
+ end
118
+
119
+ private
120
+
121
+ def fold(text)
122
+ text.strip.gsub /\s+/, ' '
123
+ end
124
+
125
+ def match_split_card
126
+ / \(.*\)/
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,50 @@
1
+ # The MIT License (MIT)
2
+ # Copyright (c) 2011 Tom Crayford
3
+ # Refer to https://rubygems.org/gems/values
4
+
5
+ module Tamiyo
6
+ class Value
7
+ def self.new(*fields, &block)
8
+ Class.new do
9
+ attr_reader(:hash, *fields)
10
+
11
+ define_method(:initialize) do |*values|
12
+ raise ArgumentError.new("wrong number of arguments, #{values.size} for #{fields.size}") if fields.size != values.size
13
+
14
+ fields.zip(values) do |field, value|
15
+ instance_variable_set(:"@#{field}", value)
16
+ end
17
+
18
+ @hash = self.class.hash ^ values.hash
19
+
20
+ freeze
21
+ end
22
+
23
+ const_set :VALUE_ATTRS, fields
24
+
25
+ def self.with(hash)
26
+ unexpected_keys = hash.keys - self::VALUE_ATTRS
27
+ if unexpected_keys.any?
28
+ raise ArgumentError.new("Unexpected hash keys: #{unexpected_keys}")
29
+ end
30
+
31
+ new(*hash.values_at(*self::VALUE_ATTRS))
32
+ end
33
+
34
+ def ==(other)
35
+ eql?(other)
36
+ end
37
+
38
+ def eql?(other)
39
+ self.class == other.class && values == other.values
40
+ end
41
+
42
+ def values
43
+ self.class::VALUE_ATTRS.map { |field| send(field) }
44
+ end
45
+
46
+ class_eval &block if block_given?
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,3 @@
1
+ module Tamiyo
2
+ VERSION = "0.0.1"
3
+ end
data/tamiyo.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'tamiyo/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'tamiyo'
8
+ spec.version = Tamiyo::VERSION
9
+ spec.authors = ['Roger Norling']
10
+ spec.email = ['roger.norling.se@gmail.com']
11
+ spec.summary = %q{Import/manage Cockatrice's card database}
12
+ spec.description = %q{A utility that import cards from Wizard's Gatherer. Allows for incremental imports and selective export to Cockatrice's card database format.}
13
+ spec.homepage = 'https://github.com/rogernorling/tamiyo#readme'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.3'
22
+ spec.add_development_dependency 'rake'
23
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tamiyo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Roger Norling
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-12-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: A utility that import cards from Wizard's Gatherer. Allows for incremental
42
+ imports and selective export to Cockatrice's card database format.
43
+ email:
44
+ - roger.norling.se@gmail.com
45
+ executables:
46
+ - tamiyo
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - ".gitignore"
51
+ - Gemfile
52
+ - LICENCE.txt
53
+ - Rakefile
54
+ - Readme.md
55
+ - bin/tamiyo
56
+ - lib/tamiyo.rb
57
+ - lib/tamiyo/card.rb
58
+ - lib/tamiyo/dummy_scrape.rb
59
+ - lib/tamiyo/dummy_scrape_chain.rb
60
+ - lib/tamiyo/gatherer_scrape.rb
61
+ - lib/tamiyo/gatherer_scrape_chain.rb
62
+ - lib/tamiyo/process.rb
63
+ - lib/tamiyo/puts_sink.rb
64
+ - lib/tamiyo/scrape_parser.rb
65
+ - lib/tamiyo/value.rb
66
+ - lib/tamiyo/version.rb
67
+ - tamiyo.gemspec
68
+ homepage: https://github.com/rogernorling/tamiyo#readme
69
+ licenses:
70
+ - MIT
71
+ metadata: {}
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubyforge_project:
88
+ rubygems_version: 2.1.11
89
+ signing_key:
90
+ specification_version: 4
91
+ summary: Import/manage Cockatrice's card database
92
+ test_files: []