xiv_lodestone 0.0.3 → 0.0.4
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 +4 -4
- data/.travis.yml +1 -0
- data/CHANGELOG.md +15 -1
- data/Gemfile +0 -4
- data/README.md +52 -11
- data/lib/xiv_lodestone/lodestone_character.rb +55 -98
- data/lib/xiv_lodestone/lodestone_character_attribute.rb +39 -0
- data/lib/xiv_lodestone/lodestone_character_disciple.rb +54 -0
- data/lib/xiv_lodestone/lodestone_character_gear.rb +78 -0
- data/lib/xiv_lodestone/lodestone_character_mount.rb +35 -0
- data/lib/xiv_lodestone/lodestone_helper.rb +134 -55
- data/lib/xiv_lodestone/version.rb +4 -4
- data/lib/xiv_lodestone.rb +2 -8
- data/spec/spec_helper.rb +12 -12
- data/spec/xiv_lodestone_spec.rb +39 -66
- data/xiv_lodestone.gemspec +1 -0
- metadata +20 -3
- data/lib/xiv_lodestone/lodestone_parser.rb +0 -158
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d232080a2b27816989b05574d6d11c40fb424766
|
4
|
+
data.tar.gz: dba67820e7bd6a137f5d28f5ec0245bfa47e400e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: daabe0be0130d7c981f18decb3fc982400fad31f526a6402d560a74282d0b3a677628f82ec4d5db2546089bfc4e3c88f05597f966341986036ded7ff28a94b65
|
7
|
+
data.tar.gz: 23e74f384445e88841081d3dc674d95a5c149b2d9fc170b9c6bd1c129ed607a8fbfda7ba62b3d47831668d693259ebb0ea571b7c988b3438c53d603406fbbaf7
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -8,16 +8,30 @@ Features:
|
|
8
8
|
- Gear List - Shows item name, ilevel, slot, ffxiv db url, calculates total ilevel
|
9
9
|
- Disciple List(Classes) - Shows class name, level, current exp, total exp, icon url, calculates experience to next level
|
10
10
|
- All character attributes
|
11
|
+
- Converts Character profile to json
|
11
12
|
|
12
13
|
## Alpha: 0.0.2 (2015-01-05)
|
13
14
|
|
14
15
|
Bugfixes:
|
15
16
|
- Added required Ruby version to gemspec
|
16
17
|
|
17
|
-
## Alpha: 0.0.3 (2015-01-
|
18
|
+
## Alpha: 0.0.3 (2015-01-05)
|
18
19
|
|
19
20
|
Bugfixes:
|
20
21
|
- Added nokogiri dependency to gemspec
|
21
22
|
- Remove rake dependency from spec
|
22
23
|
- Fixed ilevel calculation, forgot to double 2handed ilevel as offhand
|
23
24
|
- Refactored Paser#get_classes()., Parser#get_gear
|
25
|
+
|
26
|
+
## Alpha: 0.0.4 (2015-01-07)
|
27
|
+
|
28
|
+
Features
|
29
|
+
- Added new method to creating a Character class, Character.new("Benpi Kancho"), can pass name without server if you have an extremely rare name
|
30
|
+
- Added server name method
|
31
|
+
- Character, DiscipleList, GearList class have had a to_json method added to them
|
32
|
+
- Added first_name and last_name methods
|
33
|
+
- Added minion parser + mounts parser
|
34
|
+
|
35
|
+
Bugfixes
|
36
|
+
- Rewriting more comprehensive tests
|
37
|
+
- Change the way Character.new arguments work
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,12 +1,16 @@
|
|
1
1
|
# XIVLodestone - A simple FFXIV lodestone scraper [](https://travis-ci.org/benjiro/XIV-lodestone) [](https://codeclimate.com/github/benjiro/XIV-lodestone) [](https://codeclimate.com/github/benjiro/XIV-lodestone) [](https://gemnasium.com/benjiro/XIV-lodestone)
|
2
2
|
|
3
3
|
A simple API for scraping information from FFXIV lodestone community website.
|
4
|
+
The API uses a technique called metaprogramming, this means that additional
|
5
|
+
disciples added to the game will be detected and add automatically.
|
4
6
|
|
5
|
-
Please note this gem is currently in Alpha stage of development expect bugs,
|
7
|
+
Please note this gem is currently in Alpha stage of development expect bugs,
|
8
|
+
submit a issue for bug or feature requests.
|
6
9
|
|
7
10
|
## Features
|
8
11
|
- Character Search (via ID number or Name and Server)
|
9
12
|
- Character profile
|
13
|
+
- name, server, portrait image url
|
10
14
|
- HP, MP, TP
|
11
15
|
- Race, Sex, Clan, Nameday, Guardian, City, Grand Company, Free Company
|
12
16
|
- Gear List - Shows item name, ilevel, slot, ffxiv db url, calculates total ilevel
|
@@ -14,8 +18,10 @@ Please note this gem is currently in Alpha stage of development expect bugs, sub
|
|
14
18
|
- All character attributes
|
15
19
|
|
16
20
|
## TODO
|
17
|
-
-
|
18
|
-
-
|
21
|
+
- Server Status parser
|
22
|
+
- Job detection, soul crystal
|
23
|
+
- To Json functionality
|
24
|
+
- Achievements, Blogs, Friends list, Mounts, Pets
|
19
25
|
- Free Company Search/Gather Member information
|
20
26
|
- Clean up smelly code 24/7
|
21
27
|
|
@@ -37,9 +43,8 @@ Or install it yourself as:
|
|
37
43
|
|
38
44
|
## Usage
|
39
45
|
|
40
|
-
|
41
|
-
|
42
|
-
Once the gem is installed include it in your project.
|
46
|
+
This usage of the gem is pretty straight forward most of the method names are
|
47
|
+
directly taken from the lodestone naming conversation.
|
43
48
|
|
44
49
|
```ruby
|
45
50
|
require 'xiv_lodestone'
|
@@ -47,13 +52,44 @@ require 'xiv_lodestone'
|
|
47
52
|
|
48
53
|
To parse a character
|
49
54
|
```ruby
|
50
|
-
|
51
|
-
|
55
|
+
character = XIVLodestone::Character("Benpi Kancho", "Tonberry")
|
56
|
+
character = XIVLodestone::Character("Benpi Kancho")
|
57
|
+
character = XIVLodestone::Character(1549391)
|
52
58
|
```
|
53
59
|
|
54
60
|
Basic examples
|
55
61
|
|
56
62
|
```ruby
|
63
|
+
character.name #=> character full name #String
|
64
|
+
character.first_name #=> character first name #String
|
65
|
+
character.last_name #=> character last name #String
|
66
|
+
character.server #=> server name #String
|
67
|
+
character.introduction #=> character profile introduction #String
|
68
|
+
character.title #=> character title #String
|
69
|
+
character.portrait #=> character portrait image url #String
|
70
|
+
character.hp #=> character hp #Integer
|
71
|
+
character.mp #=> character mp #Integer
|
72
|
+
character.tp #=> character tp #Integer
|
73
|
+
character.sex #=> character sex #String
|
74
|
+
character.race #=> character race #String
|
75
|
+
character.clan #=> character clan #String
|
76
|
+
character.nameday #=> character nameday #String
|
77
|
+
character.guardian #=> character guardian #String
|
78
|
+
character.city #=> character home city #String
|
79
|
+
character.grand_company #=> character grand company #String
|
80
|
+
|
81
|
+
# Character Attributes
|
82
|
+
str, dex, vit, int, mnd, pie, fire, ice, wind, earth, lighting, water, accuracy,
|
83
|
+
critical_hite_rate, determination, defense, parry, magical_defense, attack_power,
|
84
|
+
skill_speed, attack_magic_potency, healing_magic_potency, spell_speed, blunt,
|
85
|
+
piercing, slashing, heavy_resistance, bind_resistance, sleep_resistance,
|
86
|
+
stun_resistance, poison_resistance, blind_resistance, silence_resistance,
|
87
|
+
slow_resistance
|
88
|
+
|
89
|
+
# Available Gear Slot
|
90
|
+
weapon, head, body, hands, belt, legs, feet, shield, necklace, earrings,
|
91
|
+
bracelets, ring1, ring2
|
92
|
+
|
57
93
|
character.gear.weapon # => Hash of below data
|
58
94
|
character.gear.weapon.name # => weapon name #String
|
59
95
|
character.gear.weapon.ilevel # => item level Integer
|
@@ -61,6 +97,11 @@ character.gear.weapon.slot # => item slot #String
|
|
61
97
|
character.gear.weapon.url # => url to ffxivdb #String
|
62
98
|
character.gear.ilevel # => Character ilevel
|
63
99
|
|
100
|
+
# Available Disciples
|
101
|
+
gladiator, marauder, archer, pugilist, lancer, rogue, conjurer, thaumaturge,
|
102
|
+
arcanist, carpenter, armorer, leatherworker, alchemist, blacksmith, goldsmith,
|
103
|
+
weaver, culinarian, miner, botanist, fisher
|
104
|
+
|
64
105
|
character.disciple.rogue # => Hash of below data
|
65
106
|
character.disciple.rogue.name # => class name #String
|
66
107
|
character.disciple.rogue.level # => current level #Integer
|
@@ -68,9 +109,6 @@ character.disciple.rogue.current_exp # => current experience #Interger
|
|
68
109
|
character.disciple.rogue.total_exp # => total experience #Integer
|
69
110
|
character.disciple.rogue.icon_url # => class icon url #String
|
70
111
|
character.disciple.rogue.next_level # => experience to required to level #Integer
|
71
|
-
|
72
|
-
character.str # => Character strength value
|
73
|
-
# More to come
|
74
112
|
```
|
75
113
|
|
76
114
|
## Contributing
|
@@ -80,3 +118,6 @@ character.str # => Character strength value
|
|
80
118
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
81
119
|
4. Push to the branch (`git push origin my-new-feature`)
|
82
120
|
5. Create a new Pull Request
|
121
|
+
|
122
|
+
If you would like to say hi, i live on Tonberry - JP you can find me on my main
|
123
|
+
Benpi Kancho most of the time.
|
@@ -1,115 +1,72 @@
|
|
1
1
|
require 'xiv_lodestone/lodestone_helper'
|
2
|
-
require 'xiv_lodestone/
|
2
|
+
require 'xiv_lodestone/lodestone_character_gear'
|
3
|
+
require 'xiv_lodestone/lodestone_character_disciple'
|
4
|
+
require 'xiv_lodestone/lodestone_character_attribute'
|
5
|
+
require 'xiv_lodestone/lodestone_character_mount'
|
3
6
|
|
4
7
|
module XIVLodestone
|
5
|
-
# A Object that
|
8
|
+
# A Object that represents a FFXIV:ARR character,
|
6
9
|
# all information is obtained from the lodestone website.
|
7
10
|
class Character
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
fail ArgumentError, "Invalid Arguments: player_id(Fixnum) or player_name(String), server_name(String)]"
|
16
|
-
end
|
17
|
-
|
18
|
-
@profile = Hash.new
|
19
|
-
@profile[:disciple] = DiscipleList.new(parser.get_classes)
|
20
|
-
@profile[:gear] = GearList.new(parser.get_gear)
|
21
|
-
@profile.merge!(parser.get_attributes)
|
22
|
-
@profile[:hp] = parser.get_hp
|
23
|
-
@profile[:mp] = parser.get_mp
|
24
|
-
@profile[:tp] = parser.get_tp
|
25
|
-
@profile[:sex] = parser.get_sex
|
26
|
-
@profile[:race] = parser.get_race
|
27
|
-
@profile[:clan] = parser.get_clan
|
28
|
-
@profile[:nameday] = parser.get_nameday
|
29
|
-
@profile[:guardian] = parser.get_guardian
|
30
|
-
@profile[:city] = parser.get_city
|
31
|
-
@profile[:grand_company] = parser.get_grand_company
|
32
|
-
@profile[:free_company] = parser.get_free_company
|
33
|
-
|
34
|
-
parser = nil #Close the reference so Nokogiri cleans up itself
|
11
|
+
# default constructor, handles styles of arguments
|
12
|
+
# 1. Character.new(:name => "CHARACTER_NAME", :server => "SERVER_NAME")
|
13
|
+
# 2. Character.new(:name => "CHARACTER_NAME")
|
14
|
+
# 3. Character.new(:id => ID_NUMBER)
|
15
|
+
def initialize(args = Hash.new)
|
16
|
+
@profile = Hash.new()
|
17
|
+
initialise_profile(Helper.process_args(args))
|
35
18
|
end
|
36
|
-
|
37
|
-
def
|
38
|
-
|
39
|
-
super
|
19
|
+
# Returns a #String with characters first name
|
20
|
+
def first_name()
|
21
|
+
@profile[:name].split(/ /).first
|
40
22
|
end
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
def
|
47
|
-
@list
|
48
|
-
gear_list.each do |key, value|
|
49
|
-
gear = Gear.new(value[0], value[1], value[2], value[3])
|
50
|
-
@list[key] = gear
|
51
|
-
end
|
23
|
+
# Returns a #String with characters last name
|
24
|
+
def last_name()
|
25
|
+
@profile[:name].split(/ /).last
|
26
|
+
end
|
27
|
+
# Returns a #Array of characters mounts
|
28
|
+
def mounts()
|
29
|
+
@mounts.list
|
52
30
|
end
|
53
|
-
#
|
54
|
-
|
55
|
-
|
56
|
-
def ilevel()
|
57
|
-
ilevel = 0
|
58
|
-
ilevel = @list[:weapon].ilevel if Helper.is_2hand_weapon(@list[:weapon].slot)
|
59
|
-
@list.each_value do |value|
|
60
|
-
ilevel += value.ilevel
|
61
|
-
end
|
62
|
-
(ilevel/13).round
|
31
|
+
# Returns a #Array of characters minions
|
32
|
+
def minions()
|
33
|
+
@minions.list
|
63
34
|
end
|
64
|
-
# Generates
|
35
|
+
# Generates missing methods from @profile hash keys
|
65
36
|
def method_missing(method)
|
66
|
-
return @
|
37
|
+
return @profile[method] if @profile.key?(method)
|
67
38
|
super
|
68
39
|
end
|
69
|
-
#
|
70
|
-
|
71
|
-
|
72
|
-
attr_reader :name, :ilevel, :slot, :url
|
73
|
-
|
74
|
-
def initialize(name, ilevel, slot, url)
|
75
|
-
@name = name
|
76
|
-
@ilevel = ilevel
|
77
|
-
@slot = slot
|
78
|
-
@url = url
|
79
|
-
end
|
40
|
+
# Uses gem Oj to dump Character Object to JSON
|
41
|
+
def to_json()
|
42
|
+
Oj.dump(@profile)
|
80
43
|
end
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
@
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
44
|
+
#### Private Methods ####
|
45
|
+
# Initialises all characters information from lodestone
|
46
|
+
def initialise_profile(page)
|
47
|
+
@profile[:name] = Helper.get_name(page)
|
48
|
+
@profile[:server] = Helper.get_server(page)
|
49
|
+
@profile[:introduction] = Helper.get_introduction(page)
|
50
|
+
@profile[:title] = Helper.get_title(page)
|
51
|
+
@profile[:portrait] = Helper.get_portrait(page)
|
52
|
+
@profile[:hp] = Helper.get_hp(page)
|
53
|
+
@profile[:mp] = Helper.get_mp(page)
|
54
|
+
@profile[:tp] = Helper.get_tp(page)
|
55
|
+
@profile[:sex] = Helper.get_sex(page)
|
56
|
+
@profile[:race] = Helper.get_race(page)
|
57
|
+
@profile[:clan] = Helper.get_clan(page)
|
58
|
+
@profile[:nameday] = Helper.get_nameday(page)
|
59
|
+
@profile[:guardian] = Helper.get_guardian(page)
|
60
|
+
@profile[:city] = Helper.get_city(page)
|
61
|
+
@profile[:grand_company] = Helper.get_grand_company(page)
|
62
|
+
@profile[:free_company] = Helper.get_free_company(page)
|
63
|
+
@profile[:disciple] = DiscipleList.new(page.xpath("//table[@class='class_list']/tr/td"))
|
64
|
+
@profile[:gear] = GearList.new(page.xpath("(//div[@class='item_detail_box'])[position() < 13]"))
|
65
|
+
@profile[:attribute] = AttributeList.new(page.xpath('//div[starts-with(@class, "param_left_area_inner")]/ul/li'))
|
66
|
+
@mounts = MountList.new(page.xpath('(//div[@class="minion_box clearfix"])[1]/a'))
|
67
|
+
@minions = MountList.new(page.xpath('(//div[@class="minion_box clearfix"])[2]/a'))
|
97
68
|
end
|
98
|
-
# A object representation of a disciple
|
99
|
-
class Disciple
|
100
|
-
attr_reader :name, :level, :current_exp, :total_exp, :icon_url
|
101
69
|
|
102
|
-
|
103
|
-
@name = name
|
104
|
-
@level = level
|
105
|
-
@current_exp = curr
|
106
|
-
@total_exp = req
|
107
|
-
@icon_url = icon
|
108
|
-
end
|
109
|
-
# Returns the required experience to the next level
|
110
|
-
def next_level()
|
111
|
-
@total_exp - @current_exp
|
112
|
-
end
|
113
|
-
end
|
70
|
+
private :initialise_profile
|
114
71
|
end
|
115
72
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'xiv_lodestone/lodestone_helper'
|
2
|
+
require 'oj'
|
3
|
+
|
4
|
+
module XIVLodestone
|
5
|
+
# A Object that represents a set of atrributes from
|
6
|
+
# lodestone website. The class parsers a given xpath
|
7
|
+
# for attributes and stores them in a Hash. All method
|
8
|
+
# names are based of names of attributes from lodestone website
|
9
|
+
class AttributeList
|
10
|
+
def initialize(attribute_path)
|
11
|
+
@attributes = Hash.new
|
12
|
+
parse_attributes(attribute_path)
|
13
|
+
end
|
14
|
+
# Generate methods from each key in the @attribute hash
|
15
|
+
def method_missing(method)
|
16
|
+
return @attributes[method] if @attributes.key?(@attributes)
|
17
|
+
super
|
18
|
+
end
|
19
|
+
# Uses OJ to dump @attribute to json
|
20
|
+
def to_json()
|
21
|
+
Oj.dump(@attributes)
|
22
|
+
end
|
23
|
+
#### Private Methods ####
|
24
|
+
# Parsers attributes from a document into @attribute hash
|
25
|
+
def parse_attributes(attribute_path)
|
26
|
+
attribute_path.each_with_index do |li, index|
|
27
|
+
# For some weird reason the first 6 elements don't use span tags like the rest.
|
28
|
+
if index < 6
|
29
|
+
@attributes[Helper.replace_downcase(li['class']).to_sym] = li.text.to_i
|
30
|
+
else
|
31
|
+
ele = li.text.split(/(?<=\D)(?=\d)/)
|
32
|
+
@attributes[Helper.replace_downcase(ele[0]).to_sym] = ele[1].to_i
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private :parse_attributes
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'oj'
|
3
|
+
|
4
|
+
module XIVLodestone
|
5
|
+
# A object representation of disciples(classes)
|
6
|
+
# The initialiser takes a hash of Disciple, that layout follows
|
7
|
+
# { :rogue => ["Rogue", 1, 0, 300, "http://..."] }
|
8
|
+
class DiscipleList
|
9
|
+
def initialize(disciple_path)
|
10
|
+
@disciple = Hash.new
|
11
|
+
parse_disciple(disciple_path)
|
12
|
+
end
|
13
|
+
# Uses gem Oj to dump DiscipleList Object to JSON
|
14
|
+
def to_json()
|
15
|
+
Oj.dump(@disciple)
|
16
|
+
end
|
17
|
+
# Generates missing methods using @disciple hash keys
|
18
|
+
def method_missing(method)
|
19
|
+
return @disciple[method] if @disciple.key?(method)
|
20
|
+
super
|
21
|
+
end
|
22
|
+
#### Private Methods ####
|
23
|
+
def parse_disciple(disciple_path)
|
24
|
+
disciple_path.each_slice(3) do |table|
|
25
|
+
next if table[0].text.empty? #skip empty cols
|
26
|
+
@disciple[table[0].text.downcase.to_sym] = Disciple.new(
|
27
|
+
table[0].text,
|
28
|
+
table[1].text.to_i,
|
29
|
+
table[2].text.split(/\//)[0].to_i,
|
30
|
+
table[2].text.split(/\//)[1].to_i,
|
31
|
+
table[0].at_css('img')['src'])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private :parse_disciple
|
36
|
+
|
37
|
+
# A object representation of a disciple
|
38
|
+
class Disciple
|
39
|
+
attr_reader :name, :level, :current_exp, :total_exp, :icon_url
|
40
|
+
|
41
|
+
def initialize(name, level, curr, req, icon)
|
42
|
+
@name = name
|
43
|
+
@level = level
|
44
|
+
@current_exp = curr
|
45
|
+
@total_exp = req
|
46
|
+
@icon_url = icon
|
47
|
+
end
|
48
|
+
# Returns the required experience to the next level
|
49
|
+
def next_level()
|
50
|
+
@total_exp - @current_exp
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'oj'
|
3
|
+
|
4
|
+
module XIVLodestone
|
5
|
+
# A Object that represents a list of Gear pieces
|
6
|
+
# The initialiser takes a hash of items in the following layout
|
7
|
+
# { :weapon => ["Fist", 110, "Weapon", "http://...."], ... }
|
8
|
+
class GearList
|
9
|
+
def initialize(gear_path)
|
10
|
+
@items = Hash.new
|
11
|
+
parse_gear(gear_path)
|
12
|
+
end
|
13
|
+
# Calculates the total gear list ilevel
|
14
|
+
# Rounds to the nearest whole number like FFXIV ingame calculation
|
15
|
+
# returns a #Integer
|
16
|
+
def ilevel()
|
17
|
+
ilevel = 0
|
18
|
+
ilevel = @items[:weapon].ilevel if Helper.is_2hand_weapon(@items[:weapon].slot)
|
19
|
+
@items.each_value do |value|
|
20
|
+
ilevel += value.ilevel
|
21
|
+
end
|
22
|
+
(ilevel/13).round
|
23
|
+
end
|
24
|
+
# Uses gem Oj to dump GearList Object to JSON
|
25
|
+
def to_json()
|
26
|
+
Oj.dump(@items)
|
27
|
+
end
|
28
|
+
# Generates access methods for each item slot
|
29
|
+
def method_missing(method)
|
30
|
+
return @items[method] if @items.key?(method)
|
31
|
+
super
|
32
|
+
end
|
33
|
+
#### Private Methods ####
|
34
|
+
# Parses each piece of gear and stores it in @items hash
|
35
|
+
def parse_gear(gear_path)
|
36
|
+
gear_path.each do |item|
|
37
|
+
@items[type(item.at_css('h3.category_name').text).to_sym] = Gear.new(
|
38
|
+
item.css('h2').text,
|
39
|
+
item.at_css('div.pt3.pb3').text.split(/ /).last.to_i,
|
40
|
+
item.at_css('h3').text,
|
41
|
+
"http://na.finalfantasyxiv.com#{item.at_css('a')['href']}")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
# Returns a string representation of item slot
|
45
|
+
def type(name)
|
46
|
+
if name =~ /(Arm|Arms|Grimoire|Primary Tool)/i
|
47
|
+
return "weapon"
|
48
|
+
elsif name =~ /Shield/i
|
49
|
+
return "shield"
|
50
|
+
elsif name.eql?("Ring")
|
51
|
+
return "ring#{ring_inc}"
|
52
|
+
else
|
53
|
+
return name.downcase
|
54
|
+
end
|
55
|
+
end
|
56
|
+
# Initialise @num and count upwards, if @num >= 2 reset to 1
|
57
|
+
# @num += 1 reset if @num >= 2
|
58
|
+
def ring_inc()
|
59
|
+
@num ||= 0
|
60
|
+
@num = 0 if @num >= 2
|
61
|
+
@num += 1
|
62
|
+
end
|
63
|
+
|
64
|
+
private :parse_gear, :type, :ring_inc
|
65
|
+
|
66
|
+
# A object representation of a peacie of gear.
|
67
|
+
class Gear
|
68
|
+
attr_reader :name, :ilevel, :slot, :url
|
69
|
+
|
70
|
+
def initialize(name, ilevel, slot, url)
|
71
|
+
@name = name
|
72
|
+
@ilevel = ilevel
|
73
|
+
@slot = slot
|
74
|
+
@url = url
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'xiv_lodestone/lodestone_helper'
|
2
|
+
require 'oj'
|
3
|
+
|
4
|
+
module XIVLodestone
|
5
|
+
class MountList
|
6
|
+
attr_reader :list
|
7
|
+
|
8
|
+
def initialize(mount_path)
|
9
|
+
@list = Array.new
|
10
|
+
parse_mount(mount_path)
|
11
|
+
end
|
12
|
+
# Uses gem Oj to dump MountList to JSON
|
13
|
+
def to_json()
|
14
|
+
Oj.dump(@list)
|
15
|
+
end
|
16
|
+
#### Private Methods ####
|
17
|
+
def parse_mount(mount_path)
|
18
|
+
mount_path.each do |mount|
|
19
|
+
@list.push(Mount.new(mount['title'],
|
20
|
+
mount.at_xpath('img')['src']))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private :parse_mount
|
25
|
+
|
26
|
+
class Mount
|
27
|
+
attr_reader :name, :icon_url
|
28
|
+
|
29
|
+
def initialize(name, icon_url)
|
30
|
+
@name = name.split.map(&:capitalize)*' '
|
31
|
+
@icon_url = icon_url
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -1,55 +1,134 @@
|
|
1
|
-
require 'nokogiri'
|
2
|
-
require 'open-uri'
|
3
|
-
|
4
|
-
module XIVLodestone
|
5
|
-
# RuntimeError if a character can't be found
|
6
|
-
class CharacterNotFound < RuntimeError
|
7
|
-
end
|
8
|
-
# RuntimeError if more than one character is found
|
9
|
-
class MoreThanOneCharacter < RuntimeError
|
10
|
-
end
|
11
|
-
# A class of helper methods
|
12
|
-
class Helper
|
13
|
-
#
|
14
|
-
#
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
#
|
23
|
-
#
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
#
|
47
|
-
def self.
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'open-uri'
|
3
|
+
|
4
|
+
module XIVLodestone
|
5
|
+
# RuntimeError if a character can't be found
|
6
|
+
class CharacterNotFound < RuntimeError
|
7
|
+
end
|
8
|
+
# RuntimeError if more than one character is found
|
9
|
+
class MoreThanOneCharacter < RuntimeError
|
10
|
+
end
|
11
|
+
# A class of helper methods
|
12
|
+
class Helper
|
13
|
+
# Validates arguments and calls corresponding method
|
14
|
+
# Invalid arguments will rase a ArgumentError
|
15
|
+
def self.process_args(args)
|
16
|
+
return Helper.open_id(args[:id]) if args.key?(:id)
|
17
|
+
return Helper.open_url(args[:name], "") if args.key?(:name)
|
18
|
+
return Helper.open_url(args[:name], args[:server]) if args.size == 2
|
19
|
+
fail ArgumentError, "Invalid arguments passed"
|
20
|
+
end
|
21
|
+
# Find a character profile from a given name and
|
22
|
+
# server. Returns a Nokogiri XML document of the
|
23
|
+
# characters page.
|
24
|
+
def self.open_url(name, server)
|
25
|
+
page = Nokogiri::HTML(open_character_url(name, server))
|
26
|
+
check_valid_url(page)
|
27
|
+
id = page.xpath('//h4/a')[0]['href'].split(/\//).last
|
28
|
+
Nokogiri::HTML(open_id_url(id))
|
29
|
+
end
|
30
|
+
# Find a characters profiles from a given id.
|
31
|
+
# Returns a Nokogiri XML document of the characters
|
32
|
+
# page.
|
33
|
+
def self.open_id(id)
|
34
|
+
Nokogiri::HTML(open_id_url(id))
|
35
|
+
end
|
36
|
+
# Checks string if two handed weapon type
|
37
|
+
# returns true if two handed weapon type
|
38
|
+
def self.is_2hand_weapon(name)
|
39
|
+
(name =~ /(Arm|Arms|Grimoire|Primary Tool)/i) ? true : false
|
40
|
+
end
|
41
|
+
# Replaces spaces wtih underscores, and downcases
|
42
|
+
# Returns a #String
|
43
|
+
def self.replace_downcase(string)
|
44
|
+
string.gsub(" ", "_").downcase
|
45
|
+
end
|
46
|
+
# Returns a #String of the character portrait
|
47
|
+
def self.get_server(page)
|
48
|
+
page.at_xpath('//h2/span').text.strip.gsub(/\(|\)/, "")
|
49
|
+
end
|
50
|
+
# Returns a #String of the character portrait
|
51
|
+
def self.get_portrait(page)
|
52
|
+
page.at_xpath('//div[@class="img_area bg_chara_264"]/img')['src']
|
53
|
+
end
|
54
|
+
# Returns a #String of the character name
|
55
|
+
def self.get_name(page)
|
56
|
+
page.at_xpath('//h2/a').text
|
57
|
+
end
|
58
|
+
# Returns a #String of character title
|
59
|
+
def self.get_title(page)
|
60
|
+
page.at_xpath('//h2/div').text
|
61
|
+
end
|
62
|
+
# Returns a #String of character introduction
|
63
|
+
def self.get_introduction(page)
|
64
|
+
page.at_xpath('//div[@class="area_inner_body txt_selfintroduction"]').text.strip!
|
65
|
+
end
|
66
|
+
# Returns a #Integer of the characters hp
|
67
|
+
def self.get_hp(page)
|
68
|
+
page.at_xpath('//li[@class="hp"]').text.to_i
|
69
|
+
end
|
70
|
+
# Returns a #Integer of the characters mp
|
71
|
+
def self.get_mp(page)
|
72
|
+
page.at_xpath('//li[@class="mp"]').text.to_i
|
73
|
+
end
|
74
|
+
# Returns a #Integer of the characters tp
|
75
|
+
def self.get_tp(page)
|
76
|
+
page.at_xpath('//li[@class="tp"]').text.to_i
|
77
|
+
end
|
78
|
+
# Returns a #String of the characters sex
|
79
|
+
# example "Male" or "Female"
|
80
|
+
def self.get_sex(page)
|
81
|
+
page.at_xpath('//div[@class="chara_profile_title"]').text.split(/\//).last =~ /♂/i ? "Male" : "Female"
|
82
|
+
end
|
83
|
+
# Returns a #String of the characters race
|
84
|
+
def self.get_race(page)
|
85
|
+
page.at_xpath('//div[@class="chara_profile_title"]').text.split(/\//).first.strip!
|
86
|
+
end
|
87
|
+
# Returns a #String of the characters clan
|
88
|
+
def self.get_clan(page)
|
89
|
+
page.at_xpath('//div[@class="chara_profile_title"]').text.split(/\//)[1].strip!
|
90
|
+
end
|
91
|
+
# Returns a #String with the nameday
|
92
|
+
def self.get_nameday(page)
|
93
|
+
page.at_xpath('(//div[@class="chara_profile_table"]/dl/dd)[1]').text
|
94
|
+
end
|
95
|
+
# Returns a #String with the guardian name
|
96
|
+
def self.get_guardian(page)
|
97
|
+
page.at_xpath('(//div[@class="chara_profile_table"]/dl/dd)[2]').text
|
98
|
+
end
|
99
|
+
# Returns a #String the city name
|
100
|
+
def self.get_city(page)
|
101
|
+
page.at_xpath('(//dd[@class="txt_name"])[1]').text
|
102
|
+
end
|
103
|
+
# Returns a #String with the grandcompany
|
104
|
+
def self.get_grand_company(page)
|
105
|
+
page.at_xpath('(//dd[@class="txt_name"])[2]').text
|
106
|
+
end
|
107
|
+
# Returns a #Array with the freecompany name and url
|
108
|
+
def self.get_free_company(page)
|
109
|
+
element = page.at_xpath('//dd[@class="txt_name"]/a')
|
110
|
+
[ element.text, "http://na.finalfantasyxiv.com#{element['href']}" ]
|
111
|
+
end
|
112
|
+
# Open a URL with the given name and server.
|
113
|
+
# Returns a file stream.
|
114
|
+
def self.open_character_url(name, server)
|
115
|
+
open('http://na.finalfantasyxiv.com/lodestone/character/' \
|
116
|
+
"?q=#{name}&worldname=#{server}")
|
117
|
+
end
|
118
|
+
# Open a URL with the given id.
|
119
|
+
# Reutrn a file stream.
|
120
|
+
def self.open_id_url(id)
|
121
|
+
open("http://na.finalfantasyxiv.com/lodestone/character/#{id}")
|
122
|
+
end
|
123
|
+
# Validates a Nokogiri document for a character.
|
124
|
+
# Throws a CharacterNotFound exception.
|
125
|
+
# Throws a MoreThanOneCharacter exception.
|
126
|
+
def self.check_valid_url(page)
|
127
|
+
fail CharacterNotFound unless page.at_xpath('//h4/a')
|
128
|
+
fail MoreThanOneCharacter if page.xpath('//h4/a').size > 1
|
129
|
+
end
|
130
|
+
|
131
|
+
private_class_method :open_character_url, :open_id_url
|
132
|
+
private_class_method :check_valid_url
|
133
|
+
end
|
134
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Stores the current version number of the gem
|
2
|
-
module XIVLodestone
|
3
|
-
VERSION = '0.0.
|
4
|
-
end
|
1
|
+
# Stores the current version number of the gem
|
2
|
+
module XIVLodestone
|
3
|
+
VERSION = '0.0.4'
|
4
|
+
end
|
data/lib/xiv_lodestone.rb
CHANGED
@@ -1,8 +1,2 @@
|
|
1
|
-
require 'xiv_lodestone/version'
|
2
|
-
require 'xiv_lodestone/
|
3
|
-
require 'xiv_lodestone/lodestone_character'
|
4
|
-
require 'xiv_lodestone/lodestone_parser'
|
5
|
-
|
6
|
-
# TODO
|
7
|
-
module XIVLodestone
|
8
|
-
end
|
1
|
+
require 'xiv_lodestone/version'
|
2
|
+
require 'xiv_lodestone/lodestone_character'
|
data/spec/spec_helper.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
require 'codeclimate-test-reporter'
|
2
|
-
CodeClimate::TestReporter.start
|
3
|
-
|
4
|
-
RSpec.configure do |config|
|
5
|
-
config.expect_with :rspec do |expectations|
|
6
|
-
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
7
|
-
end
|
8
|
-
|
9
|
-
config.mock_with :rspec do |mocks|
|
10
|
-
mocks.verify_partial_doubles = true
|
11
|
-
end
|
12
|
-
end
|
1
|
+
require 'codeclimate-test-reporter'
|
2
|
+
CodeClimate::TestReporter.start
|
3
|
+
|
4
|
+
RSpec.configure do |config|
|
5
|
+
config.expect_with :rspec do |expectations|
|
6
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
7
|
+
end
|
8
|
+
|
9
|
+
config.mock_with :rspec do |mocks|
|
10
|
+
mocks.verify_partial_doubles = true
|
11
|
+
end
|
12
|
+
end
|
data/spec/xiv_lodestone_spec.rb
CHANGED
@@ -5,7 +5,7 @@ LOCAL_FILE = File.join(File.dirname(__FILE__), "resources/character.html")
|
|
5
5
|
INVALID_FILE = File.join(File.dirname(__FILE__), "resources/invalid.html")
|
6
6
|
|
7
7
|
describe XIVLodestone::Character do
|
8
|
-
let(:character) { XIVLodestone::Character.new("Benpi Kancho", "Tonberry") }
|
8
|
+
let(:character) { XIVLodestone::Character.new(:name => "Benpi Kancho", :server => "Tonberry") }
|
9
9
|
|
10
10
|
it 'Character profile found' do
|
11
11
|
expect(character.nil?).to eql(false)
|
@@ -13,95 +13,68 @@ describe XIVLodestone::Character do
|
|
13
13
|
end
|
14
14
|
|
15
15
|
describe XIVLodestone::Helper do
|
16
|
+
let(:helper) { XIVLodestone::Helper }
|
17
|
+
|
16
18
|
it 'Find character from name and server' do
|
17
|
-
page =
|
18
|
-
expect(page.xpath('//h2/a')[0].text).to
|
19
|
+
page = helper.open_url("Benpi Kancho", "Tonberry")
|
20
|
+
expect(page.xpath('//h2/a')[0].text).to eq("Benpi Kancho")
|
19
21
|
end
|
20
22
|
|
21
23
|
it 'Invalid character name' do
|
22
|
-
expect {
|
24
|
+
expect { helper.open_url("#$%#FG", "Tonberry") }.to raise_error(URI::InvalidURIError)
|
23
25
|
end
|
24
26
|
|
25
27
|
it 'Find multiple characters exception' do
|
26
|
-
expect {
|
28
|
+
expect { helper.open_url("Benpi", "") }.to raise_error(XIVLodestone::MoreThanOneCharacter)
|
27
29
|
end
|
28
30
|
|
29
31
|
it 'No character found exception' do
|
30
|
-
expect {
|
32
|
+
expect { helper.open_url("Yoloswaggings", "Tonberry") }.to raise_error(XIVLodestone::CharacterNotFound)
|
31
33
|
end
|
32
34
|
|
33
35
|
it 'Find character from id' do
|
34
|
-
page =
|
36
|
+
page = helper.open_id("1549391")
|
35
37
|
expect(page.xpath('//h2/a')[0].text).to eql("Benpi Kancho")
|
36
38
|
end
|
37
|
-
end
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
it 'Character methods check' do
|
43
|
-
expect(character.city).to eql("Ul'dah")
|
44
|
-
# TODO Add full test of API
|
40
|
+
it 'invalid character id' do
|
41
|
+
expect { helper.open_id("154") }.to raise_error(OpenURI::HTTPError)
|
45
42
|
end
|
46
43
|
|
47
|
-
it '
|
48
|
-
|
49
|
-
expect(list.weapon.name).to eql("Fist")
|
50
|
-
expect(list.weapon.ilevel).to eql(110)
|
51
|
-
expect(list.weapon.slot).to eql("Weapon")
|
52
|
-
expect(list.weapon.url).to eql("http://...")
|
44
|
+
it 'invalid arguments' do
|
45
|
+
expect { helper.process_args(:server => "Tonberry")}. to raise_error(ArgumentError)
|
53
46
|
end
|
54
47
|
|
55
|
-
it '
|
56
|
-
|
57
|
-
expect(
|
58
|
-
expect(list.rogue.level).to eql(1)
|
59
|
-
expect(list.rogue.current_exp).to eql(0)
|
60
|
-
expect(list.rogue.total_exp).to eql(300)
|
61
|
-
expect(list.rogue.icon_url).to eql("http://...")
|
62
|
-
expect(list.rogue.next_level).to eql(300)
|
48
|
+
it 'Should/Shouldnt be a 2hand weapon' do
|
49
|
+
expect(helper.is_2hand_weapon("Two-handed Conjurer's Arm")).to be
|
50
|
+
expect(helper.is_2hand_weapon("Dogs Brekkie")).not_to be
|
63
51
|
end
|
64
|
-
end
|
65
52
|
|
66
|
-
|
67
|
-
|
68
|
-
|
53
|
+
it 'replace_downcase case test' do
|
54
|
+
expect(helper.replace_downcase("HELLO WORLD")).to eq("hello_world")
|
55
|
+
expect(helper.replace_downcase("well_yea")).to eq("well_yea")
|
56
|
+
end
|
69
57
|
|
70
|
-
it '
|
71
|
-
|
58
|
+
it 'sucessful call of get methods' do
|
59
|
+
valid = Nokogiri::HTML(open(LOCAL_FILE))
|
60
|
+
expect(helper.get_hp(valid)).to eq(4975)
|
61
|
+
expect(helper.get_mp(valid)).to eq(4819)
|
62
|
+
expect(helper.get_tp(valid)).to eq(1000)
|
63
|
+
expect(helper.get_sex(valid)).to eq("Male")
|
64
|
+
expect(helper.get_race(valid)).to eq("Miqo'te")
|
65
|
+
expect(helper.get_clan(valid)).to eq("Keeper of the Moon")
|
66
|
+
expect(helper.get_nameday(valid)).to eq("27th Sun of the 1st Astral Moon")
|
67
|
+
expect(helper.get_guardian(valid)).to eq("Oschon, the Wanderer")
|
68
|
+
expect(helper.get_city(valid)).to eq("Gridania")
|
69
|
+
expect(helper.get_grand_company(valid)).to eq("Immortal Flames/Second Flame Lieutenant")
|
70
|
+
expect(helper.get_free_company(valid)).to eq(["Nomad Moogles", "http://na.finalfantasyxiv.com/lodestone/freecompany/9233505136016403440/"])
|
72
71
|
end
|
72
|
+
end
|
73
73
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
expect(invalid.get_race()).to eql(nil)
|
80
|
-
expect(invalid.get_clan()).to eql(nil)
|
81
|
-
expect(invalid.get_nameday()).to eql(nil)
|
82
|
-
expect(invalid.get_guardian()).to eql(nil)
|
83
|
-
expect(invalid.get_city()).to eql(nil)
|
84
|
-
expect(invalid.get_grand_company()).to eql(nil)
|
85
|
-
expect(invalid.get_classes()).to eql({})
|
86
|
-
expect(invalid.get_attributes()).to eql({})
|
87
|
-
expect(invalid.get_gear()).to eql({})
|
88
|
-
end
|
89
|
-
|
90
|
-
it 'Sucessful parse methods' do
|
91
|
-
expect(parser.get_classes().count).to eql(20)
|
92
|
-
expect(parser.get_attributes().count).to eql(34)
|
93
|
-
expect(parser.get_gear().count).to eql (12)
|
94
|
-
expect(parser.get_hp()).to eql(4975)
|
95
|
-
expect(parser.get_mp()).to eql(4819)
|
96
|
-
expect(parser.get_tp()).to eql(1000)
|
97
|
-
expect(parser.get_sex()).to eql("Male")
|
98
|
-
expect(parser.get_race()).to eql("Miqo'te")
|
99
|
-
expect(parser.get_clan()).to eql("Keeper of the Moon")
|
100
|
-
expect(parser.get_nameday()).to eql("27th Sun of the 1st Astral Moon")
|
101
|
-
expect(parser.get_guardian()).to eql("Oschon, the Wanderer")
|
102
|
-
expect(parser.get_city()).to eql("Gridania")
|
103
|
-
expect(parser.get_grand_company()).to eql("Immortal Flames/Second Flame Lieutenant")
|
104
|
-
expect(parser.get_free_company()).to eql(["Nomad Moogles",
|
105
|
-
"http://na.finalfantasyxiv.com/lodestone/freecompany/9233505136016403440/"])
|
74
|
+
describe XIVLodestone::Character do
|
75
|
+
let(:character) { XIVLodestone::Character.new(:name => "Pocket Rocket", :server =>"Tonberry") }
|
76
|
+
|
77
|
+
it 'Character methods check' do
|
78
|
+
# TODO Add full test of API
|
106
79
|
end
|
107
80
|
end
|
data/xiv_lodestone.gemspec
CHANGED
@@ -21,6 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.required_ruby_version = "~> 2.2"
|
22
22
|
|
23
23
|
spec.add_dependency("nokogiri", "~> 1.6")
|
24
|
+
spec.add_dependency("oj", "~> 2.11")
|
24
25
|
|
25
26
|
spec.add_development_dependency("bundler", "~> 1.7")
|
26
27
|
spec.add_development_dependency("rspec", "~> 3.1")
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xiv_lodestone
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Benjamin Evenson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-01-
|
11
|
+
date: 2015-01-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: oj
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.11'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.11'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -84,8 +98,11 @@ files:
|
|
84
98
|
- Rakefile
|
85
99
|
- lib/xiv_lodestone.rb
|
86
100
|
- lib/xiv_lodestone/lodestone_character.rb
|
101
|
+
- lib/xiv_lodestone/lodestone_character_attribute.rb
|
102
|
+
- lib/xiv_lodestone/lodestone_character_disciple.rb
|
103
|
+
- lib/xiv_lodestone/lodestone_character_gear.rb
|
104
|
+
- lib/xiv_lodestone/lodestone_character_mount.rb
|
87
105
|
- lib/xiv_lodestone/lodestone_helper.rb
|
88
|
-
- lib/xiv_lodestone/lodestone_parser.rb
|
89
106
|
- lib/xiv_lodestone/version.rb
|
90
107
|
- spec/resources/character.html
|
91
108
|
- spec/resources/invalid.html
|
@@ -1,158 +0,0 @@
|
|
1
|
-
require 'nokogiri'
|
2
|
-
|
3
|
-
module XIVLodestone
|
4
|
-
class Parser
|
5
|
-
# An exception for a invalid Nokogiri page
|
6
|
-
class InvalidDocument < Exception
|
7
|
-
end
|
8
|
-
# default constructor, initalises @page with a
|
9
|
-
# Nokogiri document. Raises a InvalidDocument eception
|
10
|
-
# if page is invalid
|
11
|
-
def initialize(page)
|
12
|
-
fail InvalidDocument, 'Invalid page' if page.nil?
|
13
|
-
@page = page
|
14
|
-
end
|
15
|
-
# Returns a #Hash of the characters class levels
|
16
|
-
# Example { :Gladitor => 10, ... }
|
17
|
-
# If no classes found returns a empty #Hash
|
18
|
-
def get_classes()
|
19
|
-
class_list = Hash.new
|
20
|
-
@page.xpath('//table[@class="class_list"]/tr/td').each_slice(3) do |td|
|
21
|
-
# Not a valid class
|
22
|
-
next if td[0].text.empty?
|
23
|
-
exp = td[2].text.split(/\//)
|
24
|
-
|
25
|
-
class_list[td[0].text.downcase.to_sym] = [td[0].text,
|
26
|
-
td[1].text.to_i,
|
27
|
-
exp[0].to_i,
|
28
|
-
exp[1].to_i,
|
29
|
-
td[0].at_css('img')['src']]
|
30
|
-
end
|
31
|
-
class_list
|
32
|
-
end
|
33
|
-
# Returns a #Hash of the character attributes
|
34
|
-
# Example { :str => 243, ... }
|
35
|
-
# If no attributes found returns a empty #Hash
|
36
|
-
def get_attributes()
|
37
|
-
stats = Hash.new
|
38
|
-
@page.xpath('//div[starts-with(@class, "param_left_area_inner")]/ul/li').each_with_index do |li, index|
|
39
|
-
if index < 6
|
40
|
-
stats[replace_downcase(li['class']).to_sym] = li.text.to_i
|
41
|
-
else
|
42
|
-
ele = li.text.split(/(?<=\D)(?=\d)/)
|
43
|
-
stats[replace_downcase(ele[0]).to_sym] = ele[1].to_i
|
44
|
-
end
|
45
|
-
end
|
46
|
-
stats
|
47
|
-
end
|
48
|
-
# Returns a #Hash of the characters gear list
|
49
|
-
# Example { :head => [ "item_name", "item_url" ], ... }
|
50
|
-
# If no gear found returns a empty #Hash
|
51
|
-
def get_gear()
|
52
|
-
items = Hash.new
|
53
|
-
@page.xpath("(//div[@class='item_detail_box'])[position() < 13]").each do |item|
|
54
|
-
type = get_item_type(item.at_css('h3.category_name').text)
|
55
|
-
items[type.to_sym] = [ item.css('h2').text,
|
56
|
-
item.at_css('div.pt3.pb3').text.split(/ /).last.to_i,
|
57
|
-
item.at_css('h3.category_name').text,
|
58
|
-
"http://na.finalfantasyxiv.com#{item.css('a')[0]['href']}" ]
|
59
|
-
end
|
60
|
-
items
|
61
|
-
end
|
62
|
-
# Returns a #Integer of the characters hp
|
63
|
-
# otherwise returns nil
|
64
|
-
def get_hp()
|
65
|
-
hp = @page.at_xpath('//li[@class="hp"]')
|
66
|
-
hp.nil? ? nil : hp.text.to_i
|
67
|
-
end
|
68
|
-
# Returns a #Integer of the characters mp
|
69
|
-
# otherwise returns nil
|
70
|
-
def get_mp()
|
71
|
-
mp = @page.at_xpath('//li[@class="mp"]')
|
72
|
-
mp.nil? ? nil : mp.text.to_i
|
73
|
-
end
|
74
|
-
# Returns a #Integer of the characters tp
|
75
|
-
# otherwise returns nil
|
76
|
-
def get_tp()
|
77
|
-
tp = @page.at_xpath('//li[@class="tp"]')
|
78
|
-
tp.nil? ? nil : tp.text.to_i
|
79
|
-
end
|
80
|
-
# Returns a #String of the characters sex
|
81
|
-
# example "Male" or "Female"
|
82
|
-
# otherwise returns nil
|
83
|
-
def get_sex()
|
84
|
-
sex = @page.at_xpath('//div[@class="chara_profile_title"]')
|
85
|
-
if sex.nil?
|
86
|
-
nil
|
87
|
-
else
|
88
|
-
sex.text.split(/\//).last =~ /♂/i ? "Male" : "Female"
|
89
|
-
end
|
90
|
-
end
|
91
|
-
# Returns a #String of the characters race
|
92
|
-
# otherwise returns nil
|
93
|
-
def get_race()
|
94
|
-
race = @page.at_xpath('//div[@class="chara_profile_title"]')
|
95
|
-
race.nil? ? nil : race.text.split(/\//).first.strip!
|
96
|
-
end
|
97
|
-
# Returns a #String of the characters clan
|
98
|
-
# otherwise returns nil
|
99
|
-
def get_clan()
|
100
|
-
clan = @page.at_xpath('//div[@class="chara_profile_title"]')
|
101
|
-
clan.nil? ? nil : clan.text.split(/\//)[1].strip!
|
102
|
-
end
|
103
|
-
# Returns a #String with the nameday
|
104
|
-
# otherwise returns nil
|
105
|
-
def get_nameday()
|
106
|
-
nameday = @page.at_xpath('(//div[@class="chara_profile_table"]/dl/dd)[1]')
|
107
|
-
nameday.nil? ? nil : nameday.text
|
108
|
-
end
|
109
|
-
# Returns a #String with the guardian name
|
110
|
-
# otherwise returns nil
|
111
|
-
def get_guardian()
|
112
|
-
guardian = @page.at_xpath('(//div[@class="chara_profile_table"]/dl/dd)[2]')
|
113
|
-
guardian.nil? ? nil : guardian.text
|
114
|
-
end
|
115
|
-
# Returns a #String the city name
|
116
|
-
# otherwise returns nil
|
117
|
-
def get_city()
|
118
|
-
city = @page.at_xpath('(//dd[@class="txt_name"])[1]')
|
119
|
-
city.nil? ? nil : city.text
|
120
|
-
end
|
121
|
-
# Returns a #String with the grandcompany
|
122
|
-
# otherwise returns nil
|
123
|
-
def get_grand_company()
|
124
|
-
company = @page.at_xpath('(//dd[@class="txt_name"])[2]')
|
125
|
-
company.nil? ? nil : company.text
|
126
|
-
end
|
127
|
-
# Returns a #Array with the freecompany name and url
|
128
|
-
# otherwise returns nil
|
129
|
-
def get_free_company()
|
130
|
-
element = @page.at_xpath('//dd[@class="txt_name"]/a')
|
131
|
-
element.nil? ? nil : [ element.text, "http://na.finalfantasyxiv.com#{element['href']}" ]
|
132
|
-
end
|
133
|
-
# Replaces spaces wtih underscores, and downcases
|
134
|
-
# Returns a #String
|
135
|
-
def replace_downcase(string)
|
136
|
-
string.gsub(" ", "_").downcase
|
137
|
-
end
|
138
|
-
# Auto-increaments between 1 and 2
|
139
|
-
def ring
|
140
|
-
@num ||= 0
|
141
|
-
@num = 0 if @num >= 2
|
142
|
-
@num += 1
|
143
|
-
end
|
144
|
-
# Returns a string representing what item type it is
|
145
|
-
def get_item_type(item_name)
|
146
|
-
if item_name =~ /(Arm|Arms|Grimoire|Primary Tool)/i
|
147
|
-
return "weapon"
|
148
|
-
elsif item_name =~ /Shield/i
|
149
|
-
return "shield"
|
150
|
-
elsif item_name.eql?("Ring")
|
151
|
-
return "ring#{ring}"
|
152
|
-
else
|
153
|
-
return item_name.downcase
|
154
|
-
end
|
155
|
-
end
|
156
|
-
private :replace_downcase, :ring, :get_item_type
|
157
|
-
end
|
158
|
-
end
|