mlb 0.7.0 → 0.9.0

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.
Files changed (66) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +5 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +113 -103
  5. data/bin/console +10 -0
  6. data/bin/setup +6 -0
  7. data/lib/mlb/client.rb +63 -0
  8. data/lib/mlb/connection.rb +80 -0
  9. data/lib/mlb/division.rb +42 -0
  10. data/lib/mlb/divisions.rb +30 -0
  11. data/lib/mlb/error_handler.rb +53 -0
  12. data/lib/mlb/errors/bad_gateway.rb +5 -0
  13. data/lib/mlb/errors/bad_request.rb +5 -0
  14. data/lib/mlb/errors/client_error.rb +5 -0
  15. data/lib/mlb/errors/connection_exception.rb +5 -0
  16. data/lib/mlb/errors/error.rb +3 -0
  17. data/lib/mlb/errors/forbidden.rb +5 -0
  18. data/lib/mlb/errors/gateway_timeout.rb +5 -0
  19. data/lib/mlb/errors/gone.rb +5 -0
  20. data/lib/mlb/errors/http_error.rb +14 -0
  21. data/lib/mlb/errors/internal_server_error.rb +5 -0
  22. data/lib/mlb/errors/network_error.rb +5 -0
  23. data/lib/mlb/errors/not_acceptable.rb +5 -0
  24. data/lib/mlb/errors/not_found.rb +5 -0
  25. data/lib/mlb/errors/payload_too_large.rb +5 -0
  26. data/lib/mlb/errors/server_error.rb +5 -0
  27. data/lib/mlb/errors/service_unavailable.rb +5 -0
  28. data/lib/mlb/errors/too_many_redirects.rb +5 -0
  29. data/lib/mlb/errors/too_many_requests.rb +5 -0
  30. data/lib/mlb/errors/unauthorized.rb +5 -0
  31. data/lib/mlb/errors/unprocessable_entity.rb +5 -0
  32. data/lib/mlb/handedness.rb +13 -0
  33. data/lib/mlb/league.rb +66 -0
  34. data/lib/mlb/leagues.rb +30 -0
  35. data/lib/mlb/player.rb +62 -18
  36. data/lib/mlb/players.rb +25 -0
  37. data/lib/mlb/position.rb +17 -0
  38. data/lib/mlb/redirect_handler.rb +55 -0
  39. data/lib/mlb/request_builder.rb +48 -0
  40. data/lib/mlb/roster.rb +22 -0
  41. data/lib/mlb/roster_entry.rb +22 -0
  42. data/lib/mlb/season_date_info.rb +49 -0
  43. data/lib/mlb/sport.rb +31 -0
  44. data/lib/mlb/sports.rb +22 -0
  45. data/lib/mlb/status.rb +8 -0
  46. data/lib/mlb/team.rb +56 -127
  47. data/lib/mlb/teams.rb +30 -0
  48. data/lib/mlb/transaction.rb +31 -0
  49. data/lib/mlb/transactions.rb +18 -0
  50. data/lib/mlb/venue.rb +13 -0
  51. data/lib/mlb/venues.rb +30 -0
  52. data/lib/mlb/version.rb +3 -13
  53. data/lib/mlb.rb +9 -3
  54. data/sig/mlb.rbs +186 -0
  55. metadata +74 -102
  56. checksums.yaml.gz.sig +0 -0
  57. data/.yardopts +0 -5
  58. data/CONTRIBUTING.md +0 -49
  59. data/LICENSE.md +0 -20
  60. data/Rakefile +0 -48
  61. data/lib/mlb/request.rb +0 -24
  62. data/mlb.gemspec +0 -27
  63. data/spec/helper.rb +0 -32
  64. data/spec/mlb_spec.rb +0 -114
  65. data.tar.gz.sig +0 -1
  66. metadata.gz.sig +0 -0
@@ -0,0 +1,5 @@
1
+ require_relative "client_error"
2
+
3
+ module MLB
4
+ class PayloadTooLarge < ClientError; end
5
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "http_error"
2
+
3
+ module MLB
4
+ class ServerError < HTTPError; end
5
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "server_error"
2
+
3
+ module MLB
4
+ class ServiceUnavailable < ServerError; end
5
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "error"
2
+
3
+ module MLB
4
+ class TooManyRedirects < Error; end
5
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "client_error"
2
+
3
+ module MLB
4
+ class TooManyRequests < ClientError; end
5
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "client_error"
2
+
3
+ module MLB
4
+ class Unauthorized < ClientError; end
5
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "client_error"
2
+
3
+ module MLB
4
+ class UnprocessableEntity < ClientError; end
5
+ end
@@ -0,0 +1,13 @@
1
+ require "shale"
2
+
3
+ module MLB
4
+ class Handedness < Shale::Mapper
5
+ attribute :code, Shale::Type::String
6
+ attribute :description, Shale::Type::String
7
+
8
+ json do
9
+ map "code", to: :code
10
+ map "description", to: :description
11
+ end
12
+ end
13
+ end
data/lib/mlb/league.rb ADDED
@@ -0,0 +1,66 @@
1
+ require "shale"
2
+ require_relative "season_date_info"
3
+ require_relative "sport"
4
+
5
+ module MLB
6
+ class League < Shale::Mapper
7
+ include Comparable
8
+
9
+ attribute :id, Shale::Type::Integer
10
+ attribute :name, Shale::Type::String
11
+ attribute :link, Shale::Type::String
12
+ attribute :abbreviation, Shale::Type::String
13
+ attribute :name_short, Shale::Type::String
14
+ attribute :season_state, Shale::Type::String
15
+ attribute :has_wild_card, Shale::Type::Boolean
16
+ attribute :has_split_season, Shale::Type::Boolean
17
+ attribute :num_games, Shale::Type::Integer
18
+ attribute :has_playoff_points, Shale::Type::Boolean
19
+ attribute :num_teams, Shale::Type::Integer
20
+ attribute :num_wildcard_teams, Shale::Type::Integer
21
+ attribute :season_date_info, SeasonDateInfo
22
+ attribute :season, Shale::Type::Integer
23
+ attribute :org_code, Shale::Type::String
24
+ attribute :conferences_in_use, Shale::Type::Boolean
25
+ attribute :divisions_in_use, Shale::Type::Boolean
26
+ attribute :sport, Sport
27
+ attribute :sort_order, Shale::Type::Integer
28
+ attribute :active, Shale::Type::Boolean
29
+
30
+ alias_method :active?, :active
31
+ alias_method :wild_card?, :has_wild_card
32
+ alias_method :has_wildcard, :has_wild_card
33
+ alias_method :wildcard?, :has_wild_card
34
+ alias_method :split_season?, :has_split_season
35
+ alias_method :playoff_points?, :has_playoff_points
36
+ alias_method :conferences?, :conferences_in_use
37
+ alias_method :divisions?, :divisions_in_use
38
+
39
+ json do
40
+ map "id", to: :id
41
+ map "name", to: :name
42
+ map "link", to: :link
43
+ map "abbreviation", to: :abbreviation
44
+ map "nameShort", to: :name_short
45
+ map "seasonState", to: :season_state
46
+ map "hasWildCard", to: :has_wild_card
47
+ map "hasSplitSeason", to: :has_split_season
48
+ map "numGames", to: :num_games
49
+ map "hasPlayoffPoints", to: :has_playoff_points
50
+ map "numTeams", to: :num_teams
51
+ map "numWildcardTeams", to: :num_wildcard_teams
52
+ map "seasonDateInfo", to: :season_date_info
53
+ map "season", to: :season
54
+ map "orgCode", to: :org_code
55
+ map "conferencesInUse", to: :conferences_in_use
56
+ map "divisionsInUse", to: :divisions_in_use
57
+ map "sport", to: :sport
58
+ map "sortOrder", to: :sort_order
59
+ map "active", to: :active
60
+ end
61
+
62
+ def <=>(other)
63
+ sort_order <=> other.sort_order
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,30 @@
1
+ require "shale"
2
+ require "uri"
3
+ require_relative "sport"
4
+ require_relative "league"
5
+
6
+ module MLB
7
+ class Leagues < Shale::Mapper
8
+ attribute :copyright, Shale::Type::String
9
+ attribute :leagues, League, collection: true
10
+
11
+ def self.all(sport: Sport.new(id: 1))
12
+ sport_id = sport.respond_to?(:id) ? sport.id : sport
13
+ params = {sportId: sport_id}
14
+ query_string = URI.encode_www_form(params)
15
+ response = CLIENT.get("leagues?#{query_string}")
16
+ leagues = from_json(response)
17
+ leagues.leagues.sort!
18
+ end
19
+
20
+ def self.find(league, sport: Sport.new(id: 1))
21
+ id = league.respond_to?(:id) ? league.id : league
22
+ sport_id = sport.respond_to?(:id) ? sport.id : sport
23
+ params = {sportId: sport_id}
24
+ query_string = URI.encode_www_form(params)
25
+ response = CLIENT.get("leagues/#{id}?#{query_string}")
26
+ leagues = from_json(response)
27
+ leagues.leagues.first
28
+ end
29
+ end
30
+ end
data/lib/mlb/player.rb CHANGED
@@ -1,24 +1,68 @@
1
+ require "shale"
2
+ require_relative "position"
3
+ require_relative "handedness"
4
+ require_relative "team"
5
+
1
6
  module MLB
2
- class Player
3
- attr_reader :name, :number, :positions, :from, :to
7
+ class Player < Shale::Mapper
8
+ attribute :id, Shale::Type::Integer
9
+ attribute :full_name, Shale::Type::String
10
+ attribute :link, Shale::Type::String
11
+ attribute :first_name, Shale::Type::String
12
+ attribute :last_name, Shale::Type::String
13
+ attribute :primary_number, Shale::Type::Integer
14
+ attribute :birth_date, Shale::Type::Date
15
+ attribute :current_age, Shale::Type::Integer
16
+ attribute :birth_city, Shale::Type::String
17
+ attribute :birth_state_province, Shale::Type::String
18
+ attribute :birth_country, Shale::Type::String
19
+ attribute :height, Shale::Type::String
20
+ attribute :weight, Shale::Type::Integer
21
+ attribute :active, Shale::Type::Boolean
22
+ attribute :current_team, Team
23
+ attribute :primary_position, Position
24
+ attribute :use_name, Shale::Type::String
25
+ attribute :middle_name, Shale::Type::String
26
+ attribute :boxscore_name, Shale::Type::String
27
+ attribute :gender, Shale::Type::String
28
+ attribute :is_player, Shale::Type::Boolean
29
+ attribute :is_verified, Shale::Type::Boolean
30
+ attribute :draft_year, Shale::Type::Integer
31
+ attribute :mlb_debut_date, Shale::Type::Date
32
+ attribute :bat_side, Handedness
33
+ attribute :pitch_hand, Handedness
4
34
 
5
- def initialize(attributes = {})
6
- attributes.each do |key, value|
7
- instance_variable_set("@#{key}", value) if self.respond_to?(key)
8
- end
9
- end
35
+ alias_method :verified?, :is_verified
36
+ alias_method :player?, :is_player
37
+ alias_method :active?, :active
10
38
 
11
- # Returns an array of Player objects given a team roster
12
- def self.all_from_roster(players)
13
- players.select { |player| player['to'].nil? }.map do |player|
14
- new(
15
- :name => player['player'],
16
- :number => player['number'].to_i,
17
- :positions => player['position'],
18
- :from => player['from'].to_i,
19
- :to => 'Present'
20
- )
21
- end
39
+ json do
40
+ map "id", to: :id
41
+ map "fullName", to: :full_name
42
+ map "link", to: :link
43
+ map "firstName", to: :first_name
44
+ map "lastName", to: :last_name
45
+ map "primaryNumber", to: :primary_number
46
+ map "birthDate", to: :birth_date
47
+ map "currentAge", to: :current_age
48
+ map "birthCity", to: :birth_city
49
+ map "birthStateProvince", to: :birth_state_province
50
+ map "birthCountry", to: :birth_country
51
+ map "height", to: :height
52
+ map "weight", to: :weight
53
+ map "active", to: :active
54
+ map "currentTeam", to: :current_team
55
+ map "primaryPosition", to: :primary_position
56
+ map "useName", to: :use_name
57
+ map "middleName", to: :middle_name
58
+ map "boxscoreName", to: :boxscore_name
59
+ map "gender", to: :gender
60
+ map "isPlayer", to: :is_player
61
+ map "isVerified", to: :is_verified
62
+ map "draftYear", to: :draft_year
63
+ map "mlbDebutDate", to: :mlb_debut_date
64
+ map "batSide", to: :bat_side
65
+ map "pitchHand", to: :pitch_hand
22
66
  end
23
67
  end
24
68
  end
@@ -0,0 +1,25 @@
1
+ require "shale"
2
+ require "uri"
3
+ require_relative "sport"
4
+ require_relative "player"
5
+
6
+ module MLB
7
+ class Players < Shale::Mapper
8
+ attribute :copyright, Shale::Type::String
9
+ attribute :players, Player, collection: true
10
+
11
+ json do
12
+ map "copyright", to: :copyright
13
+ map "people", to: :players
14
+ end
15
+
16
+ def self.all(season: Time.now.year, sport: Sport.new(id: 1))
17
+ sport_id = sport.respond_to?(:id) ? sport.id : sport
18
+ params = {season:}
19
+ query_string = URI.encode_www_form(params)
20
+ response = CLIENT.get("sports/#{sport_id}/players?#{query_string}")
21
+ players = from_json(response)
22
+ players.players
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,17 @@
1
+ require "shale"
2
+
3
+ module MLB
4
+ class Position < Shale::Mapper
5
+ attribute :code, Shale::Type::String
6
+ attribute :name, Shale::Type::String
7
+ attribute :type, Shale::Type::String
8
+ attribute :abbreviation, Shale::Type::String
9
+
10
+ json do
11
+ map "code", to: :code
12
+ map "name", to: :name
13
+ map "type", to: :type
14
+ map "abbreviation", to: :abbreviation
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,55 @@
1
+ require "net/http"
2
+ require "uri"
3
+ require_relative "connection"
4
+ require_relative "errors/too_many_redirects"
5
+ require_relative "request_builder"
6
+
7
+ module MLB
8
+ class RedirectHandler
9
+ DEFAULT_MAX_REDIRECTS = 10
10
+
11
+ attr_accessor :max_redirects
12
+ attr_reader :connection, :request_builder
13
+
14
+ def initialize(connection: Connection.new, request_builder: RequestBuilder.new,
15
+ max_redirects: DEFAULT_MAX_REDIRECTS)
16
+ @connection = connection
17
+ @request_builder = request_builder
18
+ @max_redirects = max_redirects
19
+ end
20
+
21
+ def handle(response:, request:, base_url:, redirect_count: 0)
22
+ if response.is_a?(Net::HTTPRedirection)
23
+ raise TooManyRedirects, "Too many redirects" if redirect_count > max_redirects
24
+
25
+ new_uri = build_new_uri(response, base_url)
26
+
27
+ new_request = build_request(request, new_uri, Integer(response.code))
28
+ new_response = connection.perform(request: new_request)
29
+
30
+ handle(response: new_response, request: new_request, base_url:, redirect_count: redirect_count + 1)
31
+ else
32
+ response
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def build_new_uri(response, base_url)
39
+ location = response.fetch("location")
40
+ # If location is relative, it will join with the original base URL, otherwise it will overwrite it
41
+ URI.join(base_url, location)
42
+ end
43
+
44
+ def build_request(request, uri, response_code)
45
+ http_method, body = case response_code
46
+ in 307 | 308
47
+ [request.method.downcase.to_sym, request.body]
48
+ else
49
+ [:get, nil]
50
+ end
51
+
52
+ request_builder.build(http_method:, uri:, body:)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,48 @@
1
+ require "net/http"
2
+ require "uri"
3
+ require_relative "version"
4
+
5
+ module MLB
6
+ class RequestBuilder
7
+ DEFAULT_HEADERS = {
8
+ "Content-Type" => "application/json; charset=utf-8",
9
+ "User-Agent" => "MLB-Client/#{VERSION} #{RUBY_ENGINE}/#{RUBY_VERSION} (#{RUBY_PLATFORM})"
10
+ }.freeze
11
+ HTTP_METHODS = {
12
+ get: Net::HTTP::Get,
13
+ post: Net::HTTP::Post,
14
+ put: Net::HTTP::Put,
15
+ delete: Net::HTTP::Delete
16
+ }.freeze
17
+
18
+ def build(http_method:, uri:, body: nil, headers: {})
19
+ request = create_request(http_method:, uri:, body:)
20
+ add_headers(request:, headers:)
21
+ request
22
+ end
23
+
24
+ private
25
+
26
+ def create_request(http_method:, uri:, body:)
27
+ http_method_class = HTTP_METHODS[http_method]
28
+
29
+ raise ArgumentError, "Unsupported HTTP method: #{http_method}" unless http_method_class
30
+
31
+ escaped_uri = escape_query_params(uri)
32
+ request = http_method_class.new(escaped_uri)
33
+ request.body = body
34
+ request
35
+ end
36
+
37
+ def add_headers(request:, headers:)
38
+ DEFAULT_HEADERS.merge(headers).each do |key, value|
39
+ request.delete(key)
40
+ request.add_field(key, value)
41
+ end
42
+ end
43
+
44
+ def escape_query_params(uri)
45
+ URI(uri).tap { |u| u.query = URI.encode_www_form(URI.decode_www_form(u.query)) if u.query }
46
+ end
47
+ end
48
+ end
data/lib/mlb/roster.rb ADDED
@@ -0,0 +1,22 @@
1
+ require "shale"
2
+ require "uri"
3
+ require_relative "roster_entry"
4
+ require_relative "sport"
5
+ require_relative "team"
6
+
7
+ module MLB
8
+ class Roster < Shale::Mapper
9
+ attribute :copyright, Shale::Type::String
10
+ attribute :roster, RosterEntry, collection: true
11
+
12
+ def self.find(team:, season: Time.now.year, sport: Sport.new(id: 1))
13
+ team_id = team.respond_to?(:id) ? team.id : team
14
+ sport_id = sport.respond_to?(:id) ? sport.id : sport
15
+ params = {season:, sportId: sport_id}
16
+ query_string = URI.encode_www_form(params)
17
+ response = CLIENT.get("teams/#{team_id}/roster?#{query_string}")
18
+ roster = from_json(response)
19
+ roster.roster
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ require "shale"
2
+ require_relative "player"
3
+ require_relative "position"
4
+ require_relative "status"
5
+
6
+ module MLB
7
+ class RosterEntry < Shale::Mapper
8
+ attribute :player, Player
9
+ attribute :jersey_number, Shale::Type::Integer
10
+ attribute :position, Position
11
+ attribute :status, Status
12
+ attribute :team_id, Shale::Type::Integer
13
+
14
+ json do
15
+ map "person", to: :player
16
+ map "jerseyNumber", to: :jersey_number
17
+ map "position", to: :position
18
+ map "status", to: :status
19
+ map "parentTeamId", to: :team_id
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,49 @@
1
+ require "shale"
2
+
3
+ module MLB
4
+ class SeasonDateInfo < Shale::Mapper
5
+ attribute :season_id, Shale::Type::String
6
+ attribute :pre_season_start_date, Shale::Type::Date
7
+ attribute :pre_season_end_date, Shale::Type::Date
8
+ attribute :season_start_date, Shale::Type::Date
9
+ attribute :spring_start_date, Shale::Type::Date
10
+ attribute :spring_end_date, Shale::Type::Date
11
+ attribute :regular_season_start_date, Shale::Type::Date
12
+ attribute :last_date_1st_half, Shale::Type::Date
13
+ attribute :all_star_date, Shale::Type::Date
14
+ attribute :first_date_2nd_half, Shale::Type::Date
15
+ attribute :regular_season_end_date, Shale::Type::Date
16
+ attribute :post_season_start_date, Shale::Type::Date
17
+ attribute :post_season_end_date, Shale::Type::Date
18
+ attribute :season_end_date, Shale::Type::Date
19
+ attribute :offseason_start_date, Shale::Type::Date
20
+ attribute :off_season_end_date, Shale::Type::Date
21
+ attribute :season_level_gameday_type, Shale::Type::String
22
+ attribute :game_level_gameday_type, Shale::Type::String
23
+ attribute :qualifier_plate_appearances, Shale::Type::Float
24
+ attribute :qualifier_outs_pitched, Shale::Type::Float
25
+
26
+ json do
27
+ map "seasonId", to: :season_id
28
+ map "preSeasonStartDate", to: :pre_season_start_date
29
+ map "preSeasonEndDate", to: :pre_season_end_date
30
+ map "seasonStartDate", to: :season_start_date
31
+ map "springStartDate", to: :spring_start_date
32
+ map "springEndDate", to: :spring_end_date
33
+ map "regularSeasonStartDate", to: :regular_season_start_date
34
+ map "lastDate1stHalf", to: :last_date_1st_half
35
+ map "allStarDate", to: :all_star_date
36
+ map "firstDate2ndHalf", to: :first_date_2nd_half
37
+ map "regularSeasonEndDate", to: :regular_season_end_date
38
+ map "postSeasonStartDate", to: :post_season_start_date
39
+ map "postSeasonEndDate", to: :post_season_end_date
40
+ map "seasonEndDate", to: :season_end_date
41
+ map "offseasonStartDate", to: :offseason_start_date
42
+ map "offSeasonEndDate", to: :off_season_end_date
43
+ map "seasonLevelGamedayType", to: :season_level_gameday_type
44
+ map "gameLevelGamedayType", to: :game_level_gameday_type
45
+ map "qualifierPlateAppearances", to: :qualifier_plate_appearances
46
+ map "qualifierOutsPitched", to: :qualifier_outs_pitched
47
+ end
48
+ end
49
+ end
data/lib/mlb/sport.rb ADDED
@@ -0,0 +1,31 @@
1
+ require "shale"
2
+
3
+ module MLB
4
+ class Sport < Shale::Mapper
5
+ include Comparable
6
+
7
+ attribute :id, Shale::Type::Integer
8
+ attribute :code, Shale::Type::String
9
+ attribute :link, Shale::Type::String
10
+ attribute :name, Shale::Type::String
11
+ attribute :abbreviation, Shale::Type::String
12
+ attribute :sort_order, Shale::Type::Integer
13
+ attribute :active, Shale::Type::Boolean
14
+
15
+ alias_method :active?, :active
16
+
17
+ json do
18
+ map "id", to: :id
19
+ map "code", to: :code
20
+ map "link", to: :link
21
+ map "name", to: :name
22
+ map "abbreviation", to: :abbreviation
23
+ map "sortOrder", to: :sort_order
24
+ map "activeStatus", to: :active
25
+ end
26
+
27
+ def <=>(other)
28
+ sort_order <=> other.sort_order
29
+ end
30
+ end
31
+ end
data/lib/mlb/sports.rb ADDED
@@ -0,0 +1,22 @@
1
+ require "shale"
2
+ require_relative "sport"
3
+
4
+ module MLB
5
+ class Sports < Shale::Mapper
6
+ attribute :copyright, Shale::Type::String
7
+ attribute :sports, Sport, collection: true
8
+
9
+ def self.all
10
+ response = CLIENT.get("sports")
11
+ sports = from_json(response)
12
+ sports.sports.sort!
13
+ end
14
+
15
+ def self.find(sport)
16
+ id = sport.respond_to?(:id) ? sport.id : sport
17
+ response = CLIENT.get("sports/#{id}")
18
+ sports = from_json(response)
19
+ sports.sports.first
20
+ end
21
+ end
22
+ end
data/lib/mlb/status.rb ADDED
@@ -0,0 +1,8 @@
1
+ require "shale"
2
+
3
+ module MLB
4
+ class Status < Shale::Mapper
5
+ attribute :code, Shale::Type::String
6
+ attribute :description, Shale::Type::String
7
+ end
8
+ end