├── lib ├── rakuten_web_service │ ├── travel.rb │ ├── version.rb │ ├── kobo.rb │ ├── gora.rb │ ├── travel │ │ ├── resource.rb │ │ ├── open_struct.rb │ │ ├── search_result.rb │ │ ├── hotel.rb │ │ └── area_class.rb │ ├── ichiba │ │ ├── tag.rb │ │ ├── ranking.rb │ │ ├── shop.rb │ │ ├── tag_group.rb │ │ ├── genre.rb │ │ ├── product.rb │ │ └── item.rb │ ├── ichiba.rb │ ├── string_support.rb │ ├── books.rb │ ├── books │ │ ├── total.rb │ │ ├── genre.rb │ │ ├── dvd.rb │ │ ├── foreign_book.rb │ │ ├── game.rb │ │ ├── cd.rb │ │ ├── software.rb │ │ ├── magazine.rb │ │ ├── book.rb │ │ └── resource.rb │ ├── genre_information.rb │ ├── all_proxy.rb │ ├── kobo │ │ ├── genre.rb │ │ └── ebook.rb │ ├── gora │ │ ├── course.rb │ │ ├── plan.rb │ │ └── course_detail.rb │ ├── error.rb │ ├── recipe.rb │ ├── configuration.rb │ ├── client.rb │ ├── response.rb │ ├── recipe │ │ └── category.rb │ ├── genre.rb │ ├── search_result.rb │ └── resource.rb └── rakuten_web_service.rb ├── spec ├── fixtures │ ├── ichiba │ │ ├── item_search_with_no_items.json │ │ ├── tag_search.json │ │ └── genre_search.json │ ├── books │ │ ├── genre_search.json │ │ └── game_search_with_keyword_Ruby.json │ ├── kobo │ │ └── genre_search.json │ ├── recipe │ │ └── ranking.json │ └── gora │ │ └── course_detail_search.json ├── support │ └── fixture_suppot.rb ├── spec_helper.rb ├── rakuten_web_service │ ├── ichiba │ │ ├── tag_spec.rb │ │ ├── shop_spec.rb │ │ ├── ranking_spec.rb │ │ ├── tag_group_spec.rb │ │ ├── product_search_spec.rb │ │ ├── genre_spec.rb │ │ └── item_spec.rb │ ├── books │ │ ├── total_spec.rb │ │ ├── game_spec.rb │ │ ├── magazine_spec.rb │ │ ├── software_spec.rb │ │ ├── cd_spec.rb │ │ ├── dvd_spec.rb │ │ ├── foreign_book_spec.rb │ │ ├── book_spec.rb │ │ └── genre_spec.rb │ ├── response_spec.rb │ ├── resource_spec.rb │ ├── travel │ │ ├── open_struct_spec.rb │ │ ├── search_result_spec.rb │ │ ├── simple_hotel_search_spec.rb │ │ └── area_class_spec.rb │ ├── kobo │ │ ├── genre_spec.rb │ │ └── ebook_spec.rb │ ├── gora │ │ ├── plan_spec.rb │ │ ├── course_detail_spec.rb │ │ └── course_spec.rb │ ├── recipe_spec.rb │ ├── configuration_spec.rb │ ├── genre_information_spec.rb │ ├── genre_spec.rb │ ├── client_spec.rb │ └── recipe │ │ └── category_spec.rb └── rakuten_web_service_spec.rb ├── Gemfile ├── .codeclimate.yml ├── .gitignore ├── CONTRIBUTING.md ├── examples ├── ichiba_item_search.rb ├── recipe_search.rb ├── travel_apis.rb ├── books_item_search.rb └── gora_search.rb ├── .github ├── release.yml ├── workflows │ ├── ci.yml │ └── release.yml └── FUNDING.yml ├── bin ├── rake └── rspec ├── .vscode ├── extensions.json ├── launch.json └── tasks.json ├── Rakefile ├── LICENSE.txt ├── rakuten_web_service.gemspec ├── CODE_OF_CONDUCT.md ├── README.ja.md └── README.md /lib/rakuten_web_service/travel.rb: -------------------------------------------------------------------------------- 1 | require 'rakuten_web_service/travel/area_class' 2 | require 'rakuten_web_service/travel/hotel' 3 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RakutenWebService 4 | VERSION = '1.15.0' 5 | end 6 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/kobo.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/kobo/ebook' 4 | require 'rakuten_web_service/kobo/genre' 5 | -------------------------------------------------------------------------------- /spec/fixtures/ichiba/item_search_with_no_items.json: -------------------------------------------------------------------------------- 1 | { 2 | "count": 0, 3 | "page": 1, 4 | "first": 0, 5 | "last": 0, 6 | "hits": 0, 7 | "pageCount": 0 8 | } 9 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in rakuten_web_service.gemspec 4 | gemspec 5 | 6 | group :test do 7 | gem 'simplecov', '~> 0.17.1', require: false 8 | end 9 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/gora.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/gora/course' 4 | require 'rakuten_web_service/gora/course_detail' 5 | require 'rakuten_web_service/gora/plan' 6 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | plugins: 3 | rubocop: 4 | enabled: true 5 | duplication: 6 | enabled: true 7 | config: 8 | languages: 9 | - ruby 10 | exclude_patterns: 11 | - "bin/" 12 | - "spec/" 13 | - "examples/" 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | *.swp 4 | .bundle 5 | .config 6 | .yardoc 7 | Gemfile.lock 8 | InstalledFiles 9 | _yardoc 10 | coverage 11 | doc/ 12 | lib/bundler/man 13 | pkg 14 | rdoc 15 | spec/reports 16 | test/tmp 17 | test/version_tmp 18 | tmp 19 | .ruby-version 20 | -------------------------------------------------------------------------------- /spec/support/fixture_suppot.rb: -------------------------------------------------------------------------------- 1 | module FixtureSupport 2 | def fixture(path) 3 | fixture_path = File.expand_path(File.join(File.dirname(__FILE__), '..', 'fixtures', path)) 4 | File.read(fixture_path) 5 | end 6 | end 7 | 8 | RSpec.configure { |c| c.include FixtureSupport } 9 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute to rws-ruby-sdk 2 | 3 | 1. Fork it 4 | 2. Create your feature branch (`git checkout -b my-new-feature`) 5 | 3. Commit your changes (`git commit -am 'Add some feature'`) 6 | 4. Push to the branch (`git push origin my-new-feature`) 7 | 5. Create new Pull Request 8 | -------------------------------------------------------------------------------- /spec/fixtures/ichiba/tag_search.json: -------------------------------------------------------------------------------- 1 | { 2 | "tagGroups": [ 3 | { 4 | "tags": [ 5 | { 6 | "parentTagId": 0, 7 | "tagName": "SS", 8 | "tagId": 1000317 9 | } 10 | ], 11 | "tagGroupId": 1000041, 12 | "tagGroupName": "サイズ(S/M/L)" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /examples/ichiba_item_search.rb: -------------------------------------------------------------------------------- 1 | require 'rakuten_web_service' 2 | 3 | application_id = ARGV.shift 4 | keyword = ARGV[0..-1].join(' ') 5 | 6 | RakutenWebService.configure do |c| 7 | c.application_id = application_id 8 | end 9 | 10 | items = RakutenWebService::Ichiba::Item.search(keyword: keyword) 11 | 12 | items.first(10).each do |item| 13 | puts "#{item.name}, #{item.price} yen" 14 | end 15 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/travel/resource.rb: -------------------------------------------------------------------------------- 1 | require 'rakuten_web_service/resource' 2 | require 'rakuten_web_service/travel/search_result' 3 | 4 | module RakutenWebService 5 | module Travel 6 | class Resource < RakutenWebService::Resource 7 | def self.search(options) 8 | RakutenWebService::Travel::SearchResult.new(options, self) 9 | end 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/ichiba/tag.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/resource' 4 | 5 | module RakutenWebService 6 | module Ichiba 7 | class Tag < Resource 8 | attribute :tagId, :tagName, :parentTagId 9 | 10 | def search(params = {}) 11 | params = params.merge(tagId: id) 12 | RakutenWebService::Ichiba::Item.search(params) 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | categories: 3 | - title: Enhancements 4 | labels: 5 | - 'enhancement' 6 | 7 | - title: Bug Fixes 8 | labels: 9 | - 'bug' 10 | 11 | - title: Chores 12 | labels: 13 | - '*' 14 | exclude: 15 | labels: 16 | - dependencies 17 | authors: 18 | - dependabot 19 | 20 | - title: Dependencies 21 | labels: 22 | - dependencies 23 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | # 4 | # This file was generated by Bundler. 5 | # 6 | # The application 'rake' is installed as part of a gem, and 7 | # this file is here to facilitate running it. 8 | # 9 | 10 | require "pathname" 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 12 | Pathname.new(__FILE__).realpath) 13 | 14 | require "rubygems" 15 | require "bundler/setup" 16 | 17 | load Gem.bin_path("rake", "rake") 18 | -------------------------------------------------------------------------------- /examples/recipe_search.rb: -------------------------------------------------------------------------------- 1 | require 'rakuten_web_service' 2 | 3 | application_id = ARGV.shift 4 | keyword = ARGV[0..-1].join(' ') 5 | 6 | RakutenWebService.configure do |c| 7 | c.application_id = application_id 8 | end 9 | 10 | category = RakutenWebService::Recipe.small_categories.find { |c| c.name.match(keyword) } 11 | 12 | recipes = category.ranking 13 | 14 | recipes.first(10).each do |recipe| 15 | puts "#{recipe.title} is made by #{recipe.nickname}" 16 | end 17 | -------------------------------------------------------------------------------- /bin/rspec: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | # 4 | # This file was generated by Bundler. 5 | # 6 | # The application 'rspec' is installed as part of a gem, and 7 | # this file is here to facilitate running it. 8 | # 9 | 10 | require "pathname" 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 12 | Pathname.new(__FILE__).realpath) 13 | 14 | require "rubygems" 15 | require "bundler/setup" 16 | 17 | load Gem.bin_path("rspec-core", "rspec") 18 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/ichiba.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/version' 4 | require 'rakuten_web_service/configuration' 5 | 6 | require 'rakuten_web_service/ichiba/shop' 7 | require 'rakuten_web_service/ichiba/item' 8 | require 'rakuten_web_service/ichiba/genre' 9 | require 'rakuten_web_service/ichiba/ranking' 10 | require 'rakuten_web_service/ichiba/product' 11 | require 'rakuten_web_service/ichiba/tag_group' 12 | require 'rakuten_web_service/ichiba/tag' 13 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/string_support.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RakutenWebService 4 | module StringSupport 5 | refine String do 6 | def to_snake 7 | gsub(/([a-z]+)([A-Z]{1})/, '\1_\2').downcase 8 | end 9 | 10 | def to_camel 11 | gsub(/([a-z]{1})_([a-z]{1})/) do |matched| 12 | matched = matched.split('_') 13 | matched[0] + matched[1].capitalize 14 | end 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/ichiba/ranking.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/ichiba/item' 4 | 5 | module RakutenWebService 6 | module Ichiba 7 | class RankingItem < RakutenWebService::Ichiba::Item 8 | endpoint 'https://app.rakuten.co.jp/services/api/IchibaItem/Ranking/20220601' 9 | 10 | parser do |response| 11 | response['Items'].map { |item| RankingItem.new(item) } 12 | end 13 | 14 | attribute :rank 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/ichiba/shop.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/resource' 4 | require 'rakuten_web_service/ichiba/item' 5 | 6 | module RakutenWebService 7 | module Ichiba 8 | class Shop < Resource 9 | attribute :shopName, :shopCode, :shopUrl, :shopAffiliateUrl 10 | 11 | def items(options = {}) 12 | options = options.merge(shop_code: code) 13 | RakutenWebService::Ichiba::Item.search(options) 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/books.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/version' 4 | require 'rakuten_web_service/configuration' 5 | 6 | require 'rakuten_web_service/books/book' 7 | require 'rakuten_web_service/books/cd' 8 | require 'rakuten_web_service/books/dvd' 9 | require 'rakuten_web_service/books/foreign_book' 10 | require 'rakuten_web_service/books/magazine' 11 | require 'rakuten_web_service/books/game' 12 | require 'rakuten_web_service/books/software' 13 | require 'rakuten_web_service/books/total' 14 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/books/total.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/books/resource' 4 | 5 | module RakutenWebService 6 | module Books 7 | class Total < Books::Resource 8 | endpoint 'https://app.rakuten.co.jp/services/api/BooksTotal/Search/20170404' 9 | 10 | parser do |response| 11 | response['Items'].map do |item| 12 | resource_class = find_resource_by_genre_id(item['booksGenreId']) 13 | resource_class.new(item) 14 | end 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/genre_information.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RakutenWebService 4 | class GenreInformation 5 | attr_reader :parent, :current, :children 6 | 7 | def initialize(params, genre_class) 8 | @parent = Array(params['parent']).first 9 | @parent = genre_class.new(@parent) if @parent 10 | @current = Array(params['current']).first 11 | @current = genre_class.new(@current) if @current 12 | @children = params['children'].map { |child| genre_class.new(child['child']) } 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/all_proxy.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RakutenWebService 4 | class AllProxy 5 | include Enumerable 6 | 7 | def initialize(search_result) 8 | @search_result = search_result 9 | end 10 | 11 | def each 12 | search_result = @search_result 13 | loop do 14 | search_result.each do |resource| 15 | yield resource 16 | end 17 | break unless search_result.next_page? 18 | 19 | search_result = search_result.next_page 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /examples/travel_apis.rb: -------------------------------------------------------------------------------- 1 | require 'rakuten_web_service' 2 | 3 | application_id = ARGV.shift 4 | 5 | RakutenWebService.configure do |c| 6 | c.application_id = application_id 7 | end 8 | 9 | tokyo = RakutenWebService::Travel::AreaClass::SmallClass['tokyo'] 10 | 11 | nikotama = tokyo.children.find do |detail_area| 12 | detail_area.class_name =~ /二子玉川/ 13 | end 14 | 15 | puts "#{nikotama.class_code}: #{nikotama.class_name}" 16 | 17 | nikotama.search(responseType: 'large').first(3).each do |hotel| 18 | puts "#{hotel.basic_info['hotelName']} \n\t#{hotel.basic_info['address1']} #{hotel.basic_info['address2']}" 19 | end 20 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | 5 | // List of extensions which should be recommended for users of this workspace. 6 | "recommendations": [ 7 | "rebornix.ruby", 8 | "hbenl.vscode-test-explorer", 9 | "connorshea.vscode-ruby-test-adapter", 10 | "KoichiSasada.vscode-rdbg" 11 | ], 12 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace. 13 | "unwantedRecommendations": [ 14 | 15 | ] 16 | } -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | require 'rspec/core/rake_task' 3 | 4 | task :endpoints do 5 | require 'rakuten_web_service' 6 | require 'terminal-table' 7 | 8 | table = Terminal::Table.new(headings: %w[Resource Endpoint]) do |t| 9 | RakutenWebService::Resource.subclasses.each do |resource| 10 | t << [resource.name, resource.endpoint] unless resource.endpoint.nil? 11 | end 12 | end 13 | 14 | puts table 15 | end 16 | 17 | RSpec::Core::RakeTask.new do |t| 18 | t.pattern = 'spec/**/*_spec.rb' 19 | t.rspec_opts = '-c -fd' 20 | end 21 | 22 | task :rspec => :spec 23 | task :default => :spec 24 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/ichiba/tag_group.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/resource' 4 | 5 | module RakutenWebService 6 | module Ichiba 7 | class TagGroup < Resource 8 | endpoint 'https://app.rakuten.co.jp/services/api/IchibaTag/Search/20140222' 9 | 10 | parser do |response| 11 | response['tagGroups'].map { |tag_group| TagGroup.new(tag_group) } 12 | end 13 | 14 | attribute :tagGroupName, :tagGroupId 15 | 16 | def tags 17 | get_attribute('tags').map do |tag| 18 | Tag.new(tag) 19 | end 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/kobo/genre.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/genre' 4 | 5 | module RakutenWebService 6 | module Kobo 7 | class Genre < RakutenWebService::BaseGenre 8 | self.resource_name = :kobo_genre 9 | 10 | root_id '101' 11 | 12 | endpoint 'https://app.rakuten.co.jp/services/api/Kobo/GenreSearch/20131010' 13 | 14 | attribute :koboGenreId, :koboGenreName, :genreLevel, :itemCount 15 | 16 | def search(options = {}) 17 | options = options.merge(self.class.genre_id_key => id) 18 | RWS::Kobo::Ebook.search(options) 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/travel/open_struct.rb: -------------------------------------------------------------------------------- 1 | module RakutenWebService 2 | module Travel 3 | class OpenStruct 4 | using RakutenWebService::StringSupport 5 | 6 | def initialize(hash) 7 | @table = {} 8 | hash.each do |(key, val)| 9 | val = self.class.new(val) if val.is_a?(Hash) 10 | val = val.map { |v| self.class.new(v) } if val.is_a?(Array) 11 | name = key.to_sym 12 | @table[name] = val 13 | define_singleton_method(name) { @table[name] } 14 | define_singleton_method(name.to_s.to_snake) { @table[name] } 15 | end 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | ruby-version: 16 | - 3.1 17 | - 3.2 18 | - 3.3 19 | - 3.4 20 | - head 21 | 22 | steps: 23 | - uses: actions/checkout@v4 24 | - uses: ruby/setup-ruby@v1 25 | env: 26 | BUNDLE_WITHOUT: vscode 27 | with: 28 | ruby-version: ${{ matrix.ruby-version }} 29 | bundler-cache: true 30 | - name: Run test 31 | run: bundle exec rake 32 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/books/genre.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/genre' 4 | 5 | module RakutenWebService 6 | module Books 7 | class Genre < RakutenWebService::BaseGenre 8 | self.resource_name = 'books_genre' 9 | 10 | endpoint 'https://app.rakuten.co.jp/services/api/BooksGenre/Search/20121128' 11 | 12 | attribute :booksGenreId, :booksGenreName, :genreLevel, :itemCount 13 | 14 | root_id '000' 15 | 16 | def search(params = {}) 17 | params = params.merge(booksGenreId: id) 18 | resource = Books::Resource.find_resource_by_genre_id(id) 19 | resource.search(params) 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/gora/course.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/resource' 4 | 5 | module RakutenWebService 6 | module Gora 7 | class Course < Resource 8 | endpoint 'https://app.rakuten.co.jp/services/api/Gora/GoraGolfCourseSearch/20170623' 9 | 10 | parser do |response| 11 | response['Items'].map { |item| new(item) } 12 | end 13 | 14 | attribute :golfCourseId, :golfCourseName, :golfCourseAbbr, :golfCourseNameKana, :golfCourseCaption, 15 | :address, :latitude, :longitude, :highway, :golfCourseDetailUrl, :reserveCalUrl, :ratingUrl, 16 | :golfCourseImageUrl, :evaluation 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [satoryu] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Debug Local File", 6 | "type": "rdbg", 7 | "request": "launch", 8 | "cwd": "${workspaceRoot}", 9 | "script": "${file}", 10 | "useBundler": true, 11 | "askParameters": true 12 | }, 13 | { 14 | "name": "Debug with RSpec - active spec line only", 15 | "type": "rdbg", 16 | "request": "launch", 17 | "cwd": "${workspaceRoot}", 18 | "script": "${workspaceRoot}/bin/rspec", 19 | "useBundler": true, 20 | "args": [ 21 | "${file}:${lineNumber}" 22 | ] 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /lib/rakuten_web_service.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/configuration' 4 | 5 | module RakutenWebService 6 | def configure(&block) 7 | raise ArgumentError, 'Block is required' unless block 8 | raise ArgumentError, 'Block is required to have one argument' if block.arity != 1 9 | yield configuration 10 | 11 | configuration 12 | end 13 | 14 | def configuration 15 | @configuration ||= Configuration.new 16 | end 17 | 18 | module_function :configure, :configuration 19 | end 20 | RWS = RakutenWebService 21 | 22 | require 'rakuten_web_service/ichiba' 23 | require 'rakuten_web_service/books' 24 | require 'rakuten_web_service/travel' 25 | require 'rakuten_web_service/kobo' 26 | require 'rakuten_web_service/gora' 27 | require 'rakuten_web_service/recipe' 28 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/ichiba/genre.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/genre' 4 | require 'rakuten_web_service/ichiba/ranking' 5 | require 'rakuten_web_service/ichiba/product' 6 | 7 | module RakutenWebService 8 | module Ichiba 9 | class Genre < RakutenWebService::BaseGenre 10 | endpoint 'https://app.rakuten.co.jp/services/api/IchibaGenre/Search/20140222' 11 | 12 | attribute :genreId, :genreName, :genreLevel, :itemCount 13 | 14 | root_id 0 15 | 16 | def ranking(options = {}) 17 | options = options.merge(genre_id: id) 18 | RakutenWebService::Ichiba::RankingItem.search(options) 19 | end 20 | 21 | def products(options = {}) 22 | options = options.merge(genre_id: id) 23 | RakutenWebService::Ichiba::Product.search(options) 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/travel/search_result.rb: -------------------------------------------------------------------------------- 1 | require 'rakuten_web_service/search_result' 2 | 3 | module RakutenWebService 4 | module Travel 5 | class SearchResult < RakutenWebService::SearchResult 6 | def params_to_get_next_page 7 | @params.merge('page' => (paging_info['page'] + 1)) 8 | end 9 | 10 | using RakutenWebService::StringSupport 11 | 12 | %w[page pageCount recordCount].each do |name| 13 | method_name = name.to_snake 14 | define_method method_name do 15 | paging_info[name] 16 | end 17 | end 18 | 19 | def next_page? 20 | (page < page_count) 21 | end 22 | 23 | def next_page 24 | search(params_to_get_next_page) 25 | end 26 | 27 | private 28 | 29 | def paging_info 30 | response['pagingInfo'] 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /examples/books_item_search.rb: -------------------------------------------------------------------------------- 1 | # This is a sample script of Rakuten Books APIs. 2 | # RWS Ruby SDK supports Books API. The inteface is similar to ones Ichiba API. 3 | # If you want to search CDs dealt in Rakuten Books, you can do it with `RakutenWebService::Books::CD`. 4 | # As for other resources, there are `RakutenWebService::Books::Book`, `RakutenWebService::Books::DVD` and so on. 5 | # Please refer to the following documents if you want more detail of input/output parameters: 6 | # http://webservice.rakuten.co.jp/document/ 7 | # 8 | 9 | require 'rakuten_web_service' 10 | 11 | application_id = ARGV.shift 12 | keyword = ARGV[0..-1].join(' ') 13 | 14 | RakutenWebService.configure do |c| 15 | c.application_id = application_id 16 | end 17 | 18 | cds = RakutenWebService::Books::CD.search(title: keyword) 19 | 20 | cds.first(10).each do |cd| 21 | puts "#{cd.title} (#{cd.title_kana}):\n\t #{cd.play_list}" 22 | end 23 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | if ENV['CI'] 2 | require 'simplecov' 3 | 4 | SimpleCov.start do 5 | add_filter "/spec/" 6 | end 7 | end 8 | 9 | require File.expand_path(File.join(__dir__, '..', 'lib', 'rakuten_web_service')) 10 | 11 | require 'webmock/rspec' 12 | require 'tapp' 13 | 14 | Dir[File.expand_path(File.join(__dir__, "support/**/*.rb"))].each { |f| require f } 15 | 16 | RSpec.configure do |config| 17 | config.mock_with :rspec 18 | config.filter_run_excluding type: 'integration' if ENV['INTEGRATION'] != 'yes' 19 | 20 | config.before :suite do 21 | WebMock.disable_net_connect!(allow: "codeclimate.com") 22 | end 23 | 24 | config.before :all, type: 'integration' do 25 | WebMock.allow_net_connect! 26 | RakutenWebService.configure do |c| 27 | c.application_id = ENV['RWS_APPLICATION_ID'] 28 | end 29 | end 30 | 31 | config.after :each do 32 | WebMock.reset! 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/books/dvd.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/books/resource' 4 | 5 | module RakutenWebService 6 | module Books 7 | class DVD < Books::Resource 8 | endpoint 'https://app.rakuten.co.jp/services/api/BooksDVD/Search/20170404' 9 | 10 | attribute :title, :titleKana, :artistName, :artistNameKana, 11 | :label, :jan, :makerCode, 12 | :itemCaption, :salesDate, 13 | :itemPrice, :listPrice, 14 | :discountRate, :discountPrice, 15 | :itemUrl, :affiliateUrl, 16 | :smallImageUrl, :mediumImageUrl, :largeImageUrl, 17 | :availability, 18 | :postageFlag, :limitedFlag, 19 | :reviewCount, :reviewAverage, 20 | :booksGenreId 21 | 22 | def update_key 23 | 'jan' 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/books/foreign_book.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/resource' 4 | 5 | module RakutenWebService 6 | module Books 7 | class ForeignBook < Books::Resource 8 | endpoint 'https://app.rakuten.co.jp/services/api/BooksForeignBook/Search/20170404' 9 | 10 | attribute :title, :titleKana, :japaneseTitle, 11 | :author, :authorKana, 12 | :publishName, :isbn, :itemCaption, :salesDate, 13 | :itemPrice, :listPrice, 14 | :discountRate, :discountPrice, 15 | :itemUrl, :affiliateUrl, 16 | :smallImageUrl, :mediumImageUrl, :largeImageUrl, 17 | :availability, :postageFlag, :limitedFlag, 18 | :reviewCount, :reviewAverage, 19 | :booksGenreId 20 | 21 | def update_key 22 | 'isbn' 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/books/game.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/books/resource' 4 | 5 | module RakutenWebService 6 | module Books 7 | class Game < Books::Resource 8 | endpoint 'https://app.rakuten.co.jp/services/api/BooksGame/Search/20170404' 9 | 10 | attribute :title, :titleKana, :hardware, :jan, :makerCode, 11 | :itemCaption, 12 | :salesDate, 13 | :itemPrice, :listPrice, :discountRate, :discountPrice, 14 | :itemUrl, :affiliateUrl, 15 | :contents, :contentsKana, 16 | :smallImageUrl, :mediumImageUrl, :largeImageUrl, 17 | :availability, 18 | :postageFlag, :limitedFlag, 19 | :reviewCount, :reviewAverage, 20 | :booksGenreId 21 | 22 | def update_key 23 | 'jan' 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RakutenWebService 4 | class Error < StandardError 5 | def self.register(status_code, error) 6 | repository[status_code] = error 7 | end 8 | 9 | def self.for(response) 10 | error_class = repository[response.code.to_i] 11 | error_class.new(JSON.parse(response.body)['error_description']) 12 | end 13 | 14 | def self.repository 15 | @repository ||= {} 16 | end 17 | end 18 | 19 | class WrongParameter < Error; end 20 | Error.register(400, WrongParameter) 21 | 22 | class NotFound < Error; end 23 | Error.register(404, NotFound) 24 | 25 | class TooManyRequests < Error; end 26 | Error.register(429, TooManyRequests) 27 | 28 | class SystemError < Error; end 29 | Error.register(500, SystemError) 30 | 31 | class ServiceUnavailable < Error; end 32 | Error.register(503, ServiceUnavailable) 33 | end 34 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/books/cd.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/books/resource' 4 | 5 | module RakutenWebService 6 | module Books 7 | class CD < Books::Resource 8 | endpoint 'https://app.rakuten.co.jp/services/api/BooksCD/Search/20170404' 9 | 10 | attribute :title, :titleKana, :artistName, :artistNameKana, 11 | :label, :jan, :makerCode, 12 | :itemCaption, :playList, :salesDate, 13 | :itemPrice, :listPrice, 14 | :discountRate, :discountPrice, 15 | :itemUrl, :affiliateUrl, 16 | :smallImageUrl, :mediumImageUrl, :largeImageUrl, 17 | :availability, 18 | :postageFlag, :limitedFlag, 19 | :reviewCount, :reviewAverage, 20 | :booksGenreId 21 | 22 | def update_key 23 | 'jan' 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/books/software.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/books/resource' 4 | 5 | module RakutenWebService 6 | module Books 7 | class Software < Books::Resource 8 | endpoint 'https://app.rakuten.co.jp/services/api/BooksSoftware/Search/20170404' 9 | 10 | attribute :title, :titleKana, :os, :jan, :makerCode, 11 | :itemCaption, 12 | :salesDate, 13 | :itemPrice, :listPrice, :discountRate, :discountPrice, 14 | :itemUrl, :affiliateUrl, 15 | :contents, :contentsKana, 16 | :smallImageUrl, :mediumImageUrl, :largeImageUrl, 17 | :availability, 18 | :postageFlag, :limitedFlag, 19 | :reviewCount, :reviewAverage, 20 | :booksGenreId 21 | 22 | def update_key 23 | 'jan' 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/travel/hotel.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/travel/resource' 4 | 5 | module RakutenWebService 6 | module Travel 7 | class Hotel < RakutenWebService::Travel::Resource 8 | endpoint 'https://app.rakuten.co.jp/services/api/Travel/SimpleHotelSearch/20170426' 9 | 10 | parser do |response| 11 | response['hotels'].map do |hotel_info| 12 | Hotel.new(hotel_info) 13 | end 14 | end 15 | 16 | def initialize(params) 17 | @params = {} 18 | self.class.attribute_names.each_with_index do |key, i| 19 | @params[key] = params[i][key] if params[i] 20 | end 21 | end 22 | 23 | def self.attribute_names 24 | %w(hotelBasicInfo hotelRatingInfo hotelDetailInfo hotelFacilitiesInfo hotelPolicyInfo hotelOtherInfo) 25 | end 26 | 27 | attribute *attribute_names 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/kobo/ebook.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/resource' 4 | 5 | module RakutenWebService 6 | module Kobo 7 | class Ebook < RakutenWebService::Resource 8 | endpoint 'https://app.rakuten.co.jp/services/api/Kobo/EbookSearch/20170426' 9 | 10 | attribute :title, :titleKana, :subTitle, :seriesName, 11 | :author, :authorKana, :publisherName, 12 | :itemNumber, :itemCaption, 13 | :salesDate, :itemPrice, 14 | :itemUrl, :affiliateUrl, 15 | :smallImageUrl, :mediumImageUrl, :largeImageUrl, 16 | :reviewCount, :reviewAverage, 17 | :koboGenreId 18 | 19 | parser do |response| 20 | response['Items'].map { |i| new(i) } 21 | end 22 | 23 | def self.genre_class 24 | RakutenWebService::Kobo::Genre 25 | end 26 | 27 | def genre 28 | Kobo::Genre.new(kobo_genre_id) 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/books/magazine.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/books/resource' 4 | 5 | module RakutenWebService 6 | module Books 7 | class Magazine < Books::Resource 8 | endpoint 'https://app.rakuten.co.jp/services/api/BooksMagazine/Search/20170404' 9 | 10 | attribute :title, :titleKana, :publisherName, :jan, 11 | :itemCaption, 12 | :salesDate, :cycle, 13 | :itemPrice, :listPrice, :discountRate, :discountPrice, 14 | :itemUrl, :affiliateUrl, 15 | :contents, :contentsKana, 16 | :smallImageUrl, :mediumImageUrl, :largeImageUrl, 17 | :chirayomiUrl, 18 | :availability, 19 | :postageFlag, :limitedFlag, 20 | :reviewCount, :reviewAverage, 21 | :booksGenreId 22 | 23 | def update_key 24 | 'jan' 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/recipe.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/configuration' 4 | 5 | require 'rakuten_web_service/recipe/category' 6 | 7 | module RakutenWebService 8 | class Recipe < Resource 9 | endpoint 'https://app.rakuten.co.jp/services/api/Recipe/CategoryRanking/20170426' 10 | 11 | attribute :recipeId, :recipeTitle, :recipeUrl, 12 | :foodImageUrl, :mediumImageUrl, :smallImageUrl, 13 | :pickup, :shop, :nickname, 14 | :recipeDescription, :recipeMaterial, 15 | :recipeIndication, :recipeCost, 16 | :recipePublishday, :rank 17 | 18 | parser do |response| 19 | response['result'].map { |r| Recipe.new(r) } 20 | end 21 | 22 | def self.ranking(category_id = nil) 23 | params = {} 24 | params = params.merge(category_id: category_id) unless category_id.nil? 25 | search(params) 26 | end 27 | 28 | class << self 29 | protected :search 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/fixtures/books/genre_search.json: -------------------------------------------------------------------------------- 1 | { 2 | "current": { 3 | "booksGenreId": "000", 4 | "booksGenreName": "", 5 | "genreLevel": 0 6 | }, 7 | "children": [ 8 | { 9 | "booksGenreId": "001", 10 | "booksGenreName": "本", 11 | "genreLevel": 1 12 | }, 13 | { 14 | "booksGenreId": "002", 15 | "booksGenreName": "CD", 16 | "genreLevel": 1 17 | }, 18 | { 19 | "booksGenreId": "003", 20 | "booksGenreName": "DVD", 21 | "genreLevel": 1 22 | }, 23 | { 24 | "booksGenreId": "004", 25 | "booksGenreName": "PCソフト・周辺機器", 26 | "genreLevel": 1 27 | }, 28 | { 29 | "booksGenreId": "005", 30 | "booksGenreName": "洋書", 31 | "genreLevel": 1 32 | }, 33 | { 34 | "booksGenreId": "006", 35 | "booksGenreName": "ゲーム", 36 | "genreLevel": 1 37 | }, 38 | { 39 | "booksGenreId": "007", 40 | "booksGenreName": "雑誌", 41 | "genreLevel": 1 42 | } 43 | ], 44 | "parents": [] 45 | } 46 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/books/book.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/books/resource' 4 | 5 | module RakutenWebService 6 | module Books 7 | class Book < Books::Resource 8 | endpoint 'https://app.rakuten.co.jp/services/api/BooksBook/Search/20170404' 9 | 10 | attribute :title, :titleKana, :subTitle, :subTitleKana, 11 | :seriesName, :seriesNameKana, 12 | :contents, :contentsKana, 13 | :author, :authorKana, :publisherName, 14 | :size, :isbn, 15 | :itemCaption, :itemPrice, :listPrice, 16 | :discountRate, :discountPrice, 17 | :salesDate, 18 | :itemUrl, :affiliateUrl, 19 | :smallImageUrl, :mediumImageUrl, :largeImageUrl, 20 | :chirayomiUrl, 21 | :availability, 22 | :postageFlag, :limitedFlag, 23 | :reviewCount, :reviewAverage, 24 | :booksGenreId 25 | 26 | def update_key 27 | 'isbn' 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 - 2014 Rakuten, Inc 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/ichiba/tag_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::Ichiba::Tag do 4 | let(:params) do 5 | { 'tagId' => 100, 6 | 'tagName' => 'SS', 7 | 'parentTagId' => 1 } 8 | end 9 | let(:tag) { RakutenWebService::Ichiba::Tag.new(params) } 10 | 11 | describe '.new' do 12 | specify 'returned object should have methods to fetch values' do 13 | expect(tag.id).to eq(100) 14 | expect(tag.name).to eq('SS') 15 | expect(tag['parentTagId']).to eq(1) 16 | end 17 | end 18 | 19 | describe '#search' do 20 | context 'When no given additional parameters' do 21 | specify 'it calls Ichiba::Item.search with its tag id' do 22 | expect(RWS::Ichiba::Item).to receive(:search).with({tagId: params['tagId']}).and_return([]) 23 | 24 | tag.search 25 | end 26 | end 27 | context 'When given required parameter' do 28 | specify 'it calls Ichiba::Item.search with the given parameters and its tag id' do 29 | expect(RWS::Ichiba::Item).to receive(:search).with({keyword: 'Ruby', tagId: params['tagId']}).and_return([]) 30 | 31 | tag.search(keyword: 'Ruby') 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | tags: 6 | - 'v*.*.*' 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | 12 | if: startsWith(github.ref, 'refs/tags/') # Run only when tagged like v1.0.1 13 | 14 | permissions: 15 | contents: write 16 | id-token: write 17 | 18 | steps: 19 | - name: Checkout code 20 | uses: actions/checkout@v4 21 | 22 | - name: Set up Ruby 23 | uses: ruby/setup-ruby@v1 24 | with: 25 | ruby-version: 3.4 26 | bundler-cache: true 27 | 28 | - name: Build gem 29 | id: build 30 | run: bundle exec rake build 31 | 32 | - name: Capture package name 33 | id: package_name 34 | run: | 35 | echo "package_name=$(basename pkg/*.gem | tail -n1)" >> $GITHUB_ENV 36 | 37 | - uses: actions/upload-artifact@v4 38 | with: 39 | name: ${{env.package_name}} 40 | path: pkg/${{env.package_name}} 41 | 42 | - uses: softprops/action-gh-release@v2 43 | with: 44 | files: pkg/${{env.package_name}} 45 | generate_release_notes: true 46 | 47 | - uses: rubygems/release-gem@v1 48 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/configuration.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/string_support' 4 | 5 | module RakutenWebService 6 | class Configuration 7 | attr_accessor :application_id, :affiliate_id, :max_retries, :debug 8 | 9 | def initialize 10 | @application_id = ENV['RWS_APPLICATION_ID'] 11 | @affiliate_id = ENV['RWS_AFFILIATE_ID'] 12 | @max_retries = 5 13 | end 14 | 15 | def generate_parameters(params) 16 | convert_snake_key_to_camel_key(default_parameters.merge(params)) 17 | end 18 | 19 | def default_parameters 20 | raise 'Application ID is not defined' unless has_required_options? 21 | { application_id: application_id, affiliate_id: affiliate_id, format_version: '2' } 22 | end 23 | 24 | def has_required_options? 25 | application_id && application_id != '' 26 | end 27 | 28 | def debug_mode? 29 | ENV.key?('RWS_SDK_DEBUG') || debug 30 | end 31 | 32 | private 33 | 34 | using RakutenWebService::StringSupport 35 | 36 | def convert_snake_key_to_camel_key(params) 37 | params.inject({}) do |h, (k, v)| 38 | k = k.to_s.to_camel 39 | h[k] = v 40 | h 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/books/total_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::Books::Total do 4 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/BooksTotal/Search/20170404' } 5 | let(:affiliate_id) { 'dummy_affiliate_id' } 6 | let(:application_id) { 'dummy_application_id' } 7 | let(:expected_query) do 8 | { 9 | affiliateId: affiliate_id, 10 | applicationId: application_id, 11 | formatVersion: '2', 12 | keyword: 'Ruby' 13 | } 14 | end 15 | 16 | before do 17 | RakutenWebService.configure do |c| 18 | c.affiliate_id = affiliate_id 19 | c.application_id = application_id 20 | end 21 | end 22 | 23 | describe '.search' do 24 | before do 25 | response = JSON.parse(fixture('books/total_search_with_keyword_Ruby.json')) 26 | @expected_request = stub_request(:get, endpoint). 27 | with(query: expected_query).to_return(body: response.to_json) 28 | end 29 | 30 | specify 'call endpoint when accessing results' do 31 | items = RakutenWebService::Books::Total.search(keyword: 'Ruby') 32 | expect(@expected_request).to_not have_been_made 33 | 34 | items.first 35 | expect(@expected_request).to have_been_made.once 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/client.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'uri' 4 | require 'net/http' 5 | require 'cgi' 6 | require 'json' 7 | require 'rakuten_web_service/response' 8 | require 'rakuten_web_service/error' 9 | 10 | module RakutenWebService 11 | class Client 12 | USER_AGENT = "RakutenWebService SDK for Ruby v#{RWS::VERSION}(ruby-#{RUBY_VERSION} [#{RUBY_PLATFORM}])".freeze 13 | 14 | attr_reader :url 15 | 16 | def initialize(resource_class) 17 | @resource_class = resource_class 18 | @url = URI.parse(@resource_class.endpoint) 19 | end 20 | 21 | def get(params) 22 | params = RakutenWebService.configuration.generate_parameters(params) 23 | response = request(url.path, params) 24 | body = JSON.parse(response.body) 25 | unless response.is_a?(Net::HTTPSuccess) 26 | raise RakutenWebService::Error.for(response) 27 | end 28 | 29 | RakutenWebService::Response.new(@resource_class, body) 30 | end 31 | 32 | private 33 | 34 | def request(path, params) 35 | http = Net::HTTP.new(url.host, url.port) 36 | http.use_ssl = true 37 | if RakutenWebService.configuration.debug_mode? 38 | http.set_debug_output($stderr) 39 | end 40 | path = "#{path}?#{URI.encode_www_form(params)}" 41 | header = { 'User-Agent' => USER_AGENT } 42 | http.get(path, header) 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/ichiba/product.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/resource' 4 | require 'rakuten_web_service/ichiba/genre' 5 | 6 | module RakutenWebService 7 | module Ichiba 8 | class Product < Resource 9 | endpoint 'https://app.rakuten.co.jp/services/api/Product/Search/20170426' 10 | 11 | parser do |response| 12 | (response['Products'] || []).map { |prod| Product.new(prod) } 13 | end 14 | 15 | attribute :productId, :productName, :productNo, :brandName, 16 | :productUrlPC, :productUrlMobile, :affiliateUrl, 17 | :smallImageUrl, :mediumImageUrl, 18 | :productCaption, :releaseDate, 19 | :makerCode, :makerName, :makerNameKana, :makerNameFormal, 20 | :makerPageUrlPC, :makerPageUrlMobile, 21 | :itemCount, :salesItemCount, 22 | :usedExcludeCount, :usedExcludeSalesItemCount, 23 | :maxPrice, :salesMaxPrice, :usedExcludeMaxPrice, :usedExcludeSalesMaxPrice, 24 | :minPrice, :salesMinPrice, :usedExcludeMinPrice, :usedExcludeSalesMinPrice, 25 | :averagePrice, 26 | :reviewCount, :reviewAverage, :reviewUrlPC, :reviewUrlMobile, 27 | :rankTargetGenreId, :rankTargetProductCount, 28 | :genreId, :genreName, 29 | :ProductDetails 30 | 31 | def genre 32 | RakutenWebService::Ichiba::Genre.new(genre_id) 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/response_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::Response do 4 | 5 | describe "Pagenate helpers" do 6 | let(:resource_class) { double(:resource_class) } 7 | 8 | subject { RakutenWebService::Response.new(resource_class, json) } 9 | 10 | context "When page is less than pageCount" do 11 | let(:json) do 12 | { 13 | 'page' => 1, 'pageCount' => 2 14 | } 15 | end 16 | 17 | it { is_expected.to be_next_page } 18 | it { is_expected.to_not be_last_page } 19 | it { is_expected.to_not be_previous_page } 20 | it { is_expected.to be_first_page } 21 | end 22 | context "When page is equal to pageCount" do 23 | let(:json) do 24 | { 25 | 'page' => 2, 'pageCount' => 2 26 | } 27 | end 28 | 29 | it { is_expected.to_not be_next_page } 30 | it { is_expected.to be_last_page } 31 | it { is_expected.to be_previous_page } 32 | it { is_expected.to_not be_first_page } 33 | end 34 | context "When current page is in pages" do 35 | let(:json) do 36 | { 37 | 'page' => 2, 'pageCount' => 3 38 | } 39 | end 40 | 41 | it { is_expected.to be_next_page } 42 | it { is_expected.to_not be_last_page } 43 | it { is_expected.to be_previous_page } 44 | it { is_expected.to_not be_last_page } 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/response.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/string_support' 4 | 5 | module RakutenWebService 6 | class Response 7 | include Enumerable 8 | 9 | def initialize(resource_class, json) 10 | @resource_class = resource_class 11 | @json = json.dup 12 | end 13 | 14 | def [](key) 15 | @json[key] 16 | end 17 | 18 | def each 19 | resources.each do |resource| 20 | yield resource 21 | end 22 | end 23 | 24 | using RakutenWebService::StringSupport 25 | 26 | %w[count hits page first last carrier pageCount].each do |name| 27 | method_name = name.to_snake 28 | define_method method_name do 29 | self[name] 30 | end 31 | end 32 | 33 | def genre_information 34 | return unless @resource_class.respond_to?(:genre_class) 35 | return if self['GenreInformation'].empty? 36 | 37 | RWS::GenreInformation.new(self['GenreInformation'][0], @resource_class.genre_class) 38 | end 39 | 40 | def resources 41 | @resources ||= @resource_class.parse_response(@json) 42 | end 43 | 44 | def next_page? 45 | page && !last_page? 46 | end 47 | 48 | def previous_page? 49 | page && !first_page? 50 | end 51 | 52 | def first_page? 53 | page == 1 54 | end 55 | 56 | def last_page? 57 | page >= page_count 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /rakuten_web_service.gemspec: -------------------------------------------------------------------------------- 1 | lib = File.expand_path('../lib', __FILE__) 2 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 3 | require 'rakuten_web_service/version' 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = 'rakuten_web_service' 7 | spec.version = RakutenWebService::VERSION 8 | spec.authors = ['Tatsuya Sato'] 9 | spec.email = ['tatsuya.b.sato@mail.rakuten.com'] 10 | spec.description = 'Ruby Client for Rakuten Web Service' 11 | spec.summary = 'Ruby Client for Rakuten Web Service' 12 | spec.homepage = 'http://webservice.rakuten.co.jp/' 13 | spec.license = 'MIT' 14 | 15 | spec.files = `git ls-files`.split($/) 16 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 17 | spec.require_paths = ['lib'] 18 | spec.required_ruby_version = '>= 3.1.0' 19 | 20 | spec.add_dependency 'base64', '~> 0.2' 21 | spec.add_dependency 'bigdecimal', '~> 3.1' 22 | spec.add_dependency 'json', '~> 2.3' 23 | 24 | spec.add_development_dependency 'bundler' 25 | spec.add_development_dependency 'debug' 26 | spec.add_development_dependency 'rake', '~> 13.0' 27 | spec.add_development_dependency 'rexml', '~> 3.2' 28 | spec.add_development_dependency 'rspec', '~> 3.9' 29 | spec.add_development_dependency 'tapp', '~> 1.5.1' 30 | spec.add_development_dependency 'terminal-table', '~> 1.8.0' 31 | spec.add_development_dependency 'webmock', '~> 3.9' 32 | end 33 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/resource_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::Resource do 4 | let(:resource_class) do 5 | Class.new(RakutenWebService::Resource) do 6 | self.resource_name = 'Dummy' 7 | 8 | attribute :name, :dummySampleAttribute 9 | end 10 | end 11 | 12 | describe '.subclasses' do 13 | before do 14 | resource_class 15 | end 16 | 17 | subject { RakutenWebService::Resource.subclasses } 18 | 19 | specify 'returns all sub classes of Resource' do 20 | expect(subject).to include(resource_class) 21 | end 22 | 23 | context 'When some resources are inherited from other resource' do 24 | let(:other_resource_class) do 25 | Class.new(resource_class) do 26 | self.resource_name = 'NestedResource' 27 | 28 | attribute :name, :someOtherAttribute 29 | end 30 | end 31 | 32 | before do 33 | other_resource_class 34 | end 35 | 36 | specify 'includes nested resources' do 37 | expect(subject).to include(resource_class, other_resource_class) 38 | end 39 | end 40 | end 41 | 42 | describe '#attributes' do 43 | let(:params) do 44 | { 45 | name: 'hoge', 46 | dummySampleAttribute: 'fuga' 47 | } 48 | end 49 | 50 | subject { resource_class.new(params).attributes } 51 | 52 | it { is_expected.to match_array(params.keys.map(&:to_s)) } 53 | end 54 | end -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 4 | 5 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion. 6 | 7 | Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. 8 | 9 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. 10 | 11 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. 12 | 13 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) 14 | -------------------------------------------------------------------------------- /spec/rakuten_web_service_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService do 4 | describe '.configure' do 5 | context 'given block has one arity' do 6 | before do 7 | RakutenWebService.configure do |c| 8 | c.affiliate_id = 'dummy_affiliate_id' 9 | c.application_id = 'dummy_application_id' 10 | end 11 | end 12 | 13 | subject { RakutenWebService.configuration } 14 | 15 | describe '#affiliate_id' do 16 | subject { super().affiliate_id } 17 | it { is_expected.to eq('dummy_affiliate_id') } 18 | end 19 | 20 | describe '#application_id' do 21 | subject { super().application_id } 22 | 23 | it { is_expected.to eq('dummy_application_id') } 24 | end 25 | end 26 | 27 | context 'given block has more or less one arity' do 28 | specify 'raise ArgumentError' do 29 | expect do 30 | RakutenWebService.configure do 31 | end 32 | end.to raise_error(ArgumentError) 33 | 34 | expect do 35 | RakutenWebService.configure do |c, _| 36 | c.affiliate_id = 'dummy_affiliate_id' 37 | c.application_id = 'dummy_application_id' 38 | end 39 | end.to raise_error(ArgumentError) 40 | end 41 | end 42 | context 'call without block' do 43 | specify 'raise ArgumentError' do 44 | expect { RakutenWebService.configure }.to raise_error(ArgumentError) 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /examples/gora_search.rb: -------------------------------------------------------------------------------- 1 | # This is a sample script of Rakuten Gora APIs. 2 | # RWS Ruby SDK supports Gora API. The inteface is similar to ones Ichiba API. 3 | # If you want to search courses dealt in Rakuten Gora, you can do it with `RakutenWebService::Gora::Course`. 4 | # As for other resources, there are `RakutenWebService::Gora::CourseDetail`, `RakutenWebService::Gora::Plan` and so on. 5 | # Please refer to the following documents if you want more detail of input/output parameters: 6 | # http://webservice.rakuten.co.jp/document/ 7 | # 8 | 9 | require 'rakuten_web_service' 10 | require 'date' 11 | 12 | application_id = ARGV.shift 13 | keyword = ARGV.shift || '軽井沢' 14 | 15 | RakutenWebService.configure do |c| 16 | c.application_id = application_id 17 | end 18 | 19 | c = RakutenWebService::Gora::Course.search(keyword: keyword).first 20 | id = c.golf_course_id 21 | puts id 22 | puts c.golf_course_name 23 | puts c.address 24 | 25 | d = RakutenWebService::Gora::CourseDetail.find(id) 26 | puts d.green 27 | puts d.green_count 28 | puts d.course_distance 29 | puts d.long_driving_contest 30 | puts d.near_pin 31 | puts d.evaluation 32 | d.new_plans.each do |p| 33 | puts " #{p.month}: #{p.name}" 34 | end 35 | 36 | next_week = Date.today + 7 37 | chiba_and_kanagawa = '12,14' 38 | plans = RWS::Gora::Plan.search(areaCode: chiba_and_kanagawa, playDate: next_week.strftime('%Y-%m-%d')) 39 | plans.first(5).each { |p| 40 | puts "#{p.golf_course_id}, #{p.golf_course_name}" 41 | p.plan_info.each { |pi| 42 | puts " #{pi.plan_id}, #{pi.plan_name}, #{pi.price}" 43 | ci = pi.call_info 44 | puts " #{ci.play_date}, #{ci.stock_status}" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/ichiba/shop_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::Ichiba::Shop do 4 | let(:params) do 5 | { 'shopName' => 'Hoge Shop', 6 | 'shopCode' => 'hogeshop', 7 | 'shopUrl' => 'http://www.rakuten.co.jp/hogeshop' } 8 | end 9 | let(:shop) { RakutenWebService::Ichiba::Shop.new(params) } 10 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/IchibaItem/Search/20220601' } 11 | let(:affiliate_id) { 'dummy_affiliate_id' } 12 | let(:application_id) { 'dummy_application_id' } 13 | let(:expected_query) do 14 | { 15 | 'affiliateId' => affiliate_id, 16 | 'applicationId' => application_id, 17 | 'formatVersion' => '2', 18 | 'shopCode' => 'hogeshop' 19 | } 20 | end 21 | 22 | before do 23 | RakutenWebService.configure do |c| 24 | c.affiliate_id = affiliate_id 25 | c.application_id = application_id 26 | end 27 | end 28 | 29 | describe '.new' do 30 | specify 'returned object should have methods to fetch values' do 31 | expect(shop.name).to eq('Hoge Shop') 32 | expect(shop.code).to eq('hogeshop') 33 | expect(shop.url).to eq('http://www.rakuten.co.jp/hogeshop') 34 | end 35 | end 36 | 37 | describe '#items' do 38 | let(:response) do 39 | { 'Items' => [] } 40 | end 41 | 42 | before do 43 | @expected_request = stub_request(:get, endpoint). 44 | with(query: expected_query).to_return(body: response.to_json) 45 | end 46 | 47 | specify 'call the endpoint with the shopCode' do 48 | shop.items.first 49 | 50 | expect(@expected_request).to have_been_made 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/books/resource.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/resource' 4 | require 'rakuten_web_service/books/genre' 5 | 6 | module RakutenWebService 7 | module Books 8 | class Resource < RakutenWebService::Resource 9 | parser do |response| 10 | response['Items'].map { |item| new(item) } 11 | end 12 | 13 | def self.find_resource_by_genre_id(genre_id) 14 | case genre_id 15 | when /^001/ then RWS::Books::Book 16 | when /^002/ then RWS::Books::CD 17 | when /^003/ then RWS::Books::DVD 18 | when /^004/ then RWS::Books::Software 19 | when /^005/ then RWS::Books::ForeignBook 20 | when /^006/ then RWS::Books::Game 21 | when /^007/ then RWS::Books::Magazine 22 | end 23 | end 24 | 25 | def self.genre_class 26 | RakutenWebService::Books::Genre 27 | end 28 | 29 | def genre 30 | @genre ||= books_genre_id.split('/').map do |id| 31 | Books::Genre.new(id) 32 | end 33 | end 34 | alias genres genre 35 | 36 | def get_attribute(name) 37 | name = name.to_s 38 | update_params unless @params[name] 39 | @params[name] 40 | end 41 | 42 | private 43 | 44 | def update_params 45 | item = self.class.search(update_key => self[update_key]).first 46 | @params = item.params 47 | end 48 | 49 | def update_key 50 | raise 'This method is required to be overwritten in subclasses.' 51 | end 52 | 53 | protected 54 | 55 | def params 56 | @params.dup 57 | end 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/travel/open_struct_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'rakuten_web_service/travel/open_struct' 3 | 4 | describe RakutenWebService::Travel::OpenStruct do 5 | let(:object) { RakutenWebService::Travel::OpenStruct.new(params) } 6 | 7 | context 'Given simple hash with pairs of key-value' do 8 | let(:params) do 9 | { foo: 'bar', 'hoge' => 1, 'maxSize' => 100 } 10 | end 11 | 12 | specify 'should have interfaces with the name of a given hash keys' do 13 | expect(object).to respond_to(:foo) 14 | expect(object.foo).to eq(params[:foo]) 15 | expect(object).to respond_to(:hoge) 16 | expect(object.hoge).to eq(params['hoge']) 17 | end 18 | specify 'should generate snakecase-method name' do 19 | expect(object).to respond_to('maxSize') 20 | expect(object).to respond_to('max_size') 21 | end 22 | end 23 | 24 | context 'Given a hash including a hash' do 25 | let(:params) do 26 | { 27 | name: 'Taro', 28 | address: { country: :jp, city: :tokyo } 29 | } 30 | end 31 | 32 | specify 'the inside hash should be converted to OpenStruct' do 33 | expect(object.address.country).to eql(:jp) 34 | expect(object.address.city).to eql(:tokyo) 35 | end 36 | end 37 | 38 | context 'Giving an array of hash' do 39 | let(:params) do 40 | { 41 | array: [ 42 | { foo: 'bar' }, 43 | { hoge: 'fuga' } 44 | ] 45 | } 46 | end 47 | 48 | specify 'the array should be converted to OpenStruct' do 49 | expect(object.array[0].foo).to eql('bar') 50 | expect(object.array[1].hoge).to eql('fuga') 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/recipe/category.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RakutenWebService 4 | class Recipe < Resource 5 | 6 | def self.large_categories 7 | categories('large') 8 | end 9 | 10 | def self.medium_categories 11 | categories('medium') 12 | end 13 | 14 | def self.small_categories 15 | categories('small') 16 | end 17 | 18 | def self.categories(category_type) 19 | @categories ||= {} 20 | @categories[category_type] ||= Category.search(category_type: category_type).response['result'][category_type].map do |category| 21 | Category.new(category.merge(categoryType: category_type)) 22 | end 23 | end 24 | 25 | class << self 26 | protected :search 27 | end 28 | 29 | class Category < Resource 30 | endpoint 'https://app.rakuten.co.jp/services/api/Recipe/CategoryList/20170426' 31 | 32 | attribute :categoryId, :categoryName, :categoryUrl, :parentCategoryId, :categoryType 33 | 34 | def ranking 35 | Recipe.ranking(absolute_category_id) 36 | end 37 | 38 | def parent_category 39 | return nil if parent_category_type.nil? 40 | Recipe.categories(parent_category_type).find do |c| 41 | c.id.to_i == parent_category_id.to_i 42 | end 43 | end 44 | 45 | def absolute_category_id 46 | if parent_category 47 | [parent_category.absolute_category_id, id.to_s].join('-') 48 | else 49 | id.to_s 50 | end 51 | end 52 | 53 | private 54 | 55 | def parent_category_type 56 | case type 57 | when 'small' then 'medium' 58 | when 'medium' then 'large' 59 | end 60 | end 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/ichiba/ranking_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'rakuten_web_service/ichiba/ranking' 3 | 4 | describe RakutenWebService::Ichiba::RankingItem do 5 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/IchibaItem/Ranking/20220601' } 6 | let(:affiliate_id) { 'affiliate_id' } 7 | let(:application_id) { 'application_id' } 8 | let(:expected_query) do 9 | { 10 | affiliateId: affiliate_id, 11 | applicationId: application_id, 12 | formatVersion: '2' 13 | } 14 | end 15 | 16 | before do 17 | response = JSON.parse(fixture('ichiba/ranking_search.json')) 18 | @expected_request = stub_request(:get, endpoint). 19 | with(query: expected_query).to_return(body: response.to_json) 20 | 21 | RakutenWebService.configure do |c| 22 | c.affiliate_id = affiliate_id 23 | c.application_id = application_id 24 | end 25 | end 26 | 27 | describe '.search' do 28 | let(:expected_json) do 29 | response = JSON.parse(fixture('ichiba/ranking_search.json')) 30 | response['Items'][0] 31 | end 32 | 33 | before do 34 | @ranking_item = RakutenWebService::Ichiba::RankingItem.search({}).first 35 | end 36 | 37 | subject { @ranking_item } 38 | 39 | specify 'should call the endpoint once' do 40 | expect(@expected_request).to have_been_made.once 41 | end 42 | specify 'should be access by key' do 43 | expect(subject['itemName']).to eq(expected_json['itemName']) 44 | expect(subject['item_name']).to eq(expected_json['itemName']) 45 | end 46 | 47 | describe '#rank' do 48 | subject { super().rank } 49 | it { is_expected.to eq(1) } 50 | end 51 | 52 | describe '#name' do 53 | subject { super().name } 54 | it { is_expected.to eq(expected_json['itemName']) } 55 | end 56 | end 57 | 58 | end 59 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/genre.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/resource' 4 | 5 | module RakutenWebService 6 | class BaseGenre < RakutenWebService::Resource 7 | def self.inherited(klass) 8 | super 9 | 10 | klass.parser do |response| 11 | current = response['current'] 12 | %w[children parents brothers].each do |type| 13 | elements = Array(response[type]).map { |e| klass.new(e) } 14 | current.merge!(type => elements) 15 | end 16 | genre = klass.new(current) 17 | [genre] 18 | end 19 | end 20 | 21 | def self.new(params) 22 | case params 23 | when Integer, String 24 | return new(repository[params.to_s]) unless repository[params.to_s].nil? 25 | search(genre_id_key => params.to_s).first 26 | when Hash 27 | super 28 | end 29 | end 30 | 31 | def self.genre_id_key 32 | :"#{resource_name}_id" 33 | end 34 | 35 | def self.root_id(id = nil) 36 | @root_id = id || @root_id 37 | end 38 | 39 | def self.root 40 | new(root_id) 41 | end 42 | 43 | def self.[](id) 44 | new(repository[id.to_s] || id) 45 | end 46 | 47 | def self.[]=(id, genre) 48 | repository[id.to_s] = genre 49 | end 50 | 51 | def self.repository 52 | @repository ||= {} 53 | end 54 | 55 | def initialize(params) 56 | super 57 | self.class[id.to_s] = @params.reject { |k, _| k == 'itemCount' } 58 | end 59 | 60 | def children 61 | @params['children'] ||= self.class.search(self.class.genre_id_key => id).first.children 62 | end 63 | 64 | def brothers 65 | @params['brothers'] ||= self.class.search(self.class.genre_id_key => id).first.brothers 66 | end 67 | 68 | def parents 69 | @params['parents'] ||= self.class.search(self.class.genre_id_key => id).first.parents 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/ichiba/item.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/resource' 4 | 5 | module RakutenWebService 6 | module Ichiba 7 | class Item < Resource 8 | class << self 9 | def ranking(options={}) 10 | RakutenWebService::Ichiba::RankingItem.search(options) 11 | end 12 | 13 | def genre_class 14 | RakutenWebService::Ichiba::Genre 15 | end 16 | end 17 | 18 | endpoint 'https://app.rakuten.co.jp/services/api/IchibaItem/Search/20220601' 19 | 20 | parser do |response| 21 | (response['Items'] || []).map { |item| Item.new(item) } 22 | end 23 | 24 | attribute :itemName, :catchcopy, :itemCode, :itemPrice, 25 | :itemCaption, :itemUrl, :affiliateUrl, :imageFlag, 26 | :itemPriceBaseField, 27 | :itemPriceMax1, :itemPriceMax2, :itemPriceMax3, 28 | :itemPriceMin1, :itemPriceMin2, :itemPriceMin3, 29 | :smallImageUrls, :mediumImageUrls, 30 | :availability, :taxFlag, 31 | :postageFlag, :creditCardFlag, 32 | :shopOfTheYearFlag, 33 | :shipOverseasFlag, :shipOverseasArea, 34 | :asurakuFlag, :asurakuClosingTime, :asurakuArea, 35 | :affiliateRate, 36 | :startTime, :endTime, 37 | :reviewCount, :reviewAverage, 38 | :pointRate, :pointRateStartTime, :pointRateEndTime, 39 | :shopName, :shopCode, :shopUrl, :shopAffiliateUrl, 40 | :genreId, :tagIds 41 | 42 | def genre 43 | Genre.new(genre_id) 44 | end 45 | 46 | def shop 47 | Shop.new( 48 | 'shopName' => shop_name, 49 | 'shopCode' => shop_code, 50 | 'shopUrl' => shop_url, 51 | 'shopAffiliateUrl' => shop_affiliate_url 52 | ) 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/books/game_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::Books::Game do 4 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/BooksGame/Search/20170404' } 5 | let(:affiliate_id) { 'dummy_affiliate_id' } 6 | let(:application_id) { 'dummy_application_id' } 7 | let(:expected_query) do 8 | { 9 | affiliateId: affiliate_id, 10 | applicationId: application_id, 11 | formatVersion: '2', 12 | keyword: 'Ruby' 13 | } 14 | end 15 | 16 | before do 17 | RakutenWebService.configure do |c| 18 | c.affiliate_id = affiliate_id 19 | c.application_id = application_id 20 | end 21 | end 22 | 23 | describe '.search' do 24 | before do 25 | response = JSON.parse(fixture('books/game_search_with_keyword_Ruby.json')) 26 | @expected_request = stub_request(:get, endpoint). 27 | with(query: expected_query).to_return(body: response.to_json) 28 | end 29 | 30 | specify 'call endpoint when accessing results' do 31 | games = RakutenWebService::Books::Game.search(keyword: 'Ruby') 32 | expect(@expected_request).to_not have_been_made 33 | 34 | game = games.first 35 | expect(@expected_request).to have_been_made.once 36 | expect(game).to be_a(RWS::Books::Game) 37 | end 38 | end 39 | 40 | context 'When using Books::Total.search' do 41 | let(:game) do 42 | RWS::Books::Game.new(jan: '12345') 43 | end 44 | 45 | before do 46 | @expected_request = stub_request(:get, endpoint). 47 | with(query: { affiliateId: affiliate_id, applicationId: application_id, formatVersion: '2', jan: '12345' }). 48 | to_return(body: { Items: [ { title: 'foo' } ] }.to_json) 49 | end 50 | 51 | specify 'retrieves automatically if accessing the value of lack attribute' do 52 | expect(game.title).to eq('foo') 53 | expect(@expected_request).to have_been_made.once 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/books/magazine_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::Books::Magazine do 4 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/BooksMagazine/Search/20170404' } 5 | let(:affiliate_id) { 'dummy_affiliate_id' } 6 | let(:application_id) { 'dummy_application_id' } 7 | let(:expected_query) do 8 | { 9 | affiliateId: affiliate_id, 10 | applicationId: application_id, 11 | formatVersion: '2', 12 | keyword: 'Ruby' 13 | } 14 | end 15 | 16 | before do 17 | RakutenWebService.configure do |c| 18 | c.affiliate_id = affiliate_id 19 | c.application_id = application_id 20 | end 21 | end 22 | 23 | describe '.search' do 24 | before do 25 | response = JSON.parse(fixture('books/magazine_search_with_keyword_Ruby.json')) 26 | @expected_request = stub_request(:get, endpoint). 27 | with(query: expected_query).to_return(body: response.to_json) 28 | end 29 | 30 | specify 'call endpoint when accessing results' do 31 | magazines = RakutenWebService::Books::Magazine.search(keyword: 'Ruby') 32 | expect(@expected_request).to_not have_been_made 33 | 34 | magazine = magazines.first 35 | expect(@expected_request).to have_been_made.once 36 | expect(magazine).to be_a(RWS::Books::Magazine) 37 | end 38 | end 39 | 40 | context 'When using Books::Total.search' do 41 | let(:magazine) do 42 | RWS::Books::Magazine.new(jan: '12345') 43 | end 44 | 45 | before do 46 | @expected_request = stub_request(:get, endpoint). 47 | with(query: { affiliateId: affiliate_id, applicationId: application_id, formatVersion: '2', jan: '12345' }). 48 | to_return(body: { Items: [ { title: 'foo' } ] }.to_json) 49 | end 50 | 51 | specify 'retrieves automatically if accessing the value of lack attribute' do 52 | expect(magazine.title).to eq('foo') 53 | expect(@expected_request).to have_been_made.once 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/books/software_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::Books::Software do 4 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/BooksSoftware/Search/20170404' } 5 | let(:affiliate_id) { 'dummy_affiliate_id' } 6 | let(:application_id) { 'dummy_application_id' } 7 | let(:expected_query) do 8 | { 9 | affiliateId: affiliate_id, 10 | applicationId: application_id, 11 | formatVersion: '2', 12 | keyword: 'Ruby' 13 | } 14 | end 15 | 16 | before do 17 | RakutenWebService.configure do |c| 18 | c.affiliate_id = affiliate_id 19 | c.application_id = application_id 20 | end 21 | end 22 | 23 | describe '.search' do 24 | before do 25 | response = JSON.parse(fixture('books/software_search_with_keyword_Ruby.json')) 26 | @expected_request = stub_request(:get, endpoint). 27 | with(query: expected_query).to_return(body: response.to_json) 28 | end 29 | 30 | specify 'call endpoint when accessing results' do 31 | softwares = RakutenWebService::Books::Software.search(keyword: 'Ruby') 32 | expect(@expected_request).to_not have_been_made 33 | 34 | software = softwares.first 35 | expect(@expected_request).to have_been_made.once 36 | expect(software).to be_a(RWS::Books::Software) 37 | end 38 | end 39 | 40 | context 'When using Books::Total.search' do 41 | let(:software) do 42 | RWS::Books::Software.new(jan: '12345') 43 | end 44 | 45 | before do 46 | @expected_request = stub_request(:get, endpoint). 47 | with(query: { affiliateId: affiliate_id, applicationId: application_id, formatVersion: '2', jan: '12345' }). 48 | to_return(body: { Items: [ { title: 'foo' } ] }.to_json) 49 | end 50 | 51 | specify 'retrieves automatically if accessing the value of lack attribute' do 52 | expect(software.title).to eq('foo') 53 | expect(@expected_request).to have_been_made.once 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/gora/plan.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/resource' 4 | 5 | module RakutenWebService 6 | module Gora 7 | class Plan < Resource 8 | endpoint 'https://app.rakuten.co.jp/services/api/Gora/GoraPlanSearch/20170623' 9 | 10 | parser do |response| 11 | response['Items'].map { |item| new(item) } 12 | end 13 | 14 | attribute :golfCourseId, :golfCourseName, :golfCourseAbbr, :golfCourseNameKana, :golfCourseCaption, 15 | :address, :latitude, :longitude, :highway, :golfCourseDetailUrl, :reserveCalUrl, :ratingUrl, 16 | :golfCourseImageUrl, :evaluation 17 | 18 | def plan_info 19 | get_attribute('planInfo').map { |plan| PlanInfo.new(plan['plan']) } 20 | end 21 | 22 | class PlanInfo < Resource 23 | class << self 24 | def search(_options) 25 | raise 'There is no API endpoint for this resource.' 26 | end 27 | end 28 | attribute :planId, :planName, :planType, :limitedTimeFlag, :price, :basePrice, :salesTax, :courseUseTax, 29 | :otherTax, :playerNumMin, :playerNumMax, :startTimeZone, :round, :caddie, :cart, :assu2sum,:addFee2bFlag, 30 | :addFee2b, :assortment2bFlag, :addFee3bFlag, :addFee3b, :assortment3bFlag, :discount4sumFlag, :lunch, 31 | :drink, :stay, :lesson, :planOpenCompe, :compePlayGroupMin, :compePlayMemberMin, :compePrivilegeFree, 32 | :compeOption, :other, :pointFlag, :point 33 | 34 | def call_info 35 | CallInfo.new(get_attribute('callInfo')) 36 | end 37 | 38 | class CallInfo < Resource 39 | class << self 40 | def search(_options) 41 | raise 'There is no API endpoint for this resource.' 42 | end 43 | end 44 | attribute :playDate, :stockStatus, :stockCount, :reservePageUrlPC, :reservePageUrlMobile 45 | end 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/kobo/genre_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RWS::Kobo::Genre do 4 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/Kobo/GenreSearch/20131010' } 5 | let(:affiliate_id) { 'dummy_affiliate_id' } 6 | let(:application_id) { 'dummy_application_id' } 7 | let(:genre_id) { '101' } 8 | let(:expected_query) do 9 | { 10 | affiliateId: affiliate_id, 11 | applicationId: application_id, 12 | formatVersion: '2', 13 | koboGenreId: genre_id 14 | } 15 | end 16 | let(:expected_json) do 17 | JSON.parse(fixture('kobo/genre_search.json')) 18 | end 19 | 20 | before do 21 | @expected_request = stub_request(:get, endpoint). 22 | with(query: expected_query). 23 | to_return(body: expected_json.to_json) 24 | 25 | RakutenWebService.configure do |c| 26 | c.affiliate_id = affiliate_id 27 | c.application_id = application_id 28 | end 29 | end 30 | 31 | describe '.search' do 32 | before do 33 | @genre = RWS::Kobo::Genre.search(koboGenreId: genre_id).first 34 | end 35 | 36 | specify 'call the endpoint once' do 37 | expect(@expected_request).to have_been_made.once 38 | end 39 | end 40 | 41 | describe '#search' do 42 | before do 43 | stub_request(:get, endpoint).with(query: expected_query). 44 | to_return(body: expected_json.to_json) 45 | end 46 | 47 | context 'Without arguments' do 48 | specify 'should call RWS::Kobo::Ebook.search with specifying genre id' do 49 | expect(RWS::Kobo::Ebook).to receive(:search).with({RWS::Kobo::Genre.genre_id_key => genre_id}) 50 | 51 | RWS::Kobo::Genre.root.search 52 | end 53 | end 54 | context 'With arguments' do 55 | specify 'should call RWS::Kobo::Ebook.search with given arguments and genre id' do 56 | options = { title: 'Ruby' } 57 | expect(RWS::Kobo::Ebook).to receive(:search).with({title: 'Ruby', RWS::Kobo::Genre.genre_id_key => genre_id}) 58 | 59 | RWS::Kobo::Genre.root.search(options) 60 | end 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/travel/search_result_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::Travel::SearchResult do 4 | let(:resource_class) do 5 | Class.new(RakutenWebService::Resource) do 6 | endpoint 'https://api.example.com/SearchDummyResource' 7 | end 8 | end 9 | let(:search_result) do 10 | RakutenWebService::Travel::SearchResult.new({}, resource_class) 11 | end 12 | 13 | describe '#next_page?' do 14 | let(:response) do 15 | double().tap do |d| 16 | allow(d).to receive('[]').with('pagingInfo').and_return(pagingInfo) 17 | end 18 | end 19 | 20 | before do 21 | allow(search_result).to receive(:response).and_return(response) 22 | end 23 | 24 | context 'when current page does not reach at the last page' do 25 | let(:pagingInfo) do 26 | { 'page' => 1, 'pageCount' => 10 } 27 | end 28 | 29 | it 'should have next page' do 30 | expect(search_result).to be_next_page 31 | end 32 | end 33 | context 'when current page reaches at the last page' do 34 | let(:pagingInfo) do 35 | { 'page' => 5, 'pageCount' => 5 } 36 | end 37 | 38 | it 'should not have next page' do 39 | expect(search_result).to_not be_next_page 40 | end 41 | end 42 | end 43 | 44 | describe '#next_page' do 45 | let(:response) do 46 | double().tap do |d| 47 | allow(d).to receive('[]').with('pagingInfo').and_return(pagingInfo) 48 | end 49 | end 50 | 51 | before do 52 | allow(search_result).to receive(:response).and_return(response) 53 | end 54 | 55 | let(:pagingInfo) do 56 | { 'page' => 2, 'pageCount' => 3 } 57 | end 58 | 59 | it 'shold call search to fetch next page results.' do 60 | expect(search_result).to receive(:search).with({'page' => 3}) 61 | 62 | search_result.next_page 63 | end 64 | end 65 | 66 | describe '#search' do 67 | it 'should return trave\'s search result' do 68 | expect(search_result.search({})).to be_a(RakutenWebService::Travel::SearchResult) 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/books/cd_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::Books::CD do 4 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/BooksCD/Search/20170404' } 5 | let(:affiliate_id) { 'dummy_affiliate_id' } 6 | let(:application_id) { 'dummy_application_id' } 7 | let(:expected_query) do 8 | { 9 | affiliateId: affiliate_id, 10 | applicationId: application_id, 11 | formatVersion: '2', 12 | keyword: 'Ruby' 13 | } 14 | end 15 | 16 | before do 17 | RakutenWebService.configure do |c| 18 | c.affiliate_id = affiliate_id 19 | c.application_id = application_id 20 | end 21 | end 22 | 23 | describe '.search' do 24 | before do 25 | response = JSON.parse(fixture('books/cd_search_with_keyword_Ruby.json')) 26 | @expected_request = stub_request(:get, endpoint). 27 | with(query: expected_query).to_return(body: response.to_json) 28 | 29 | response['page'] = 2 30 | response['first'] = 31 31 | response['last'] = 60 32 | @second_request = stub_request(:get, endpoint). 33 | with(query: expected_query.merge(page: 2)). 34 | to_return(body: response.to_json) 35 | end 36 | 37 | specify 'call endpoint when accessing results' do 38 | cds = RakutenWebService::Books::CD.search(keyword: 'Ruby') 39 | expect(@expected_request).to_not have_been_made 40 | 41 | cd = cds.first 42 | expect(@expected_request).to have_been_made.once 43 | expect(cd).to be_a(RWS::Books::CD) 44 | end 45 | end 46 | 47 | context 'When using Books::Total.search' do 48 | let(:cd) do 49 | RWS::Books::CD.new(jan: '12345') 50 | end 51 | 52 | before do 53 | @expected_request = stub_request(:get, endpoint). 54 | with(query: { affiliateId: affiliate_id, applicationId: application_id, formatVersion: '2', jan: '12345' }). 55 | to_return(body: { Items: [ { title: 'foo' } ] }.to_json) 56 | end 57 | 58 | specify 'retrieves automatically if accessing the value of lack attribute' do 59 | expect(cd.title).to eq('foo') 60 | expect(@expected_request).to have_been_made.once 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/books/dvd_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::Books::DVD do 4 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/BooksDVD/Search/20170404' } 5 | let(:affiliate_id) { 'dummy_affiliate_id' } 6 | let(:application_id) { 'dummy_application_id' } 7 | let(:expected_query) do 8 | { 9 | affiliateId: affiliate_id, 10 | applicationId: application_id, 11 | formatVersion: '2', 12 | keyword: 'Ruby' 13 | } 14 | end 15 | 16 | before do 17 | RakutenWebService.configure do |c| 18 | c.affiliate_id = affiliate_id 19 | c.application_id = application_id 20 | end 21 | end 22 | 23 | describe '.search' do 24 | before do 25 | response = JSON.parse(fixture('books/dvd_search_with_keyword_Ruby.json')) 26 | @expected_request = stub_request(:get, endpoint). 27 | with(query: expected_query).to_return(body: response.to_json) 28 | 29 | response['page'] = 2 30 | response['first'] = 31 31 | response['last'] = 60 32 | @second_request = stub_request(:get, endpoint). 33 | with(query: expected_query.merge(page: 2)). 34 | to_return(body: response.to_json) 35 | end 36 | 37 | specify 'call endpoint when accessing results' do 38 | dvds = RakutenWebService::Books::DVD.search(keyword: 'Ruby') 39 | expect(@expected_request).to_not have_been_made 40 | 41 | dvd = dvds.first 42 | expect(@expected_request).to have_been_made.once 43 | expect(dvd).to be_a(RWS::Books::DVD) 44 | end 45 | end 46 | 47 | context 'When using Books::Total.search' do 48 | let(:dvd) do 49 | RWS::Books::DVD.new(jan: '12345') 50 | end 51 | 52 | before do 53 | @expected_request = stub_request(:get, endpoint). 54 | with(query: { affiliateId: affiliate_id, applicationId: application_id, formatVersion: '2', jan: '12345' }). 55 | to_return(body: { Items: [ { title: 'foo' } ] }.to_json) 56 | end 57 | 58 | specify 'retrieves automatically if accessing the value of lack attribute' do 59 | expect(dvd.title).to eq('foo') 60 | expect(@expected_request).to have_been_made.once 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/books/foreign_book_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::Books::ForeignBook do 4 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/BooksForeignBook/Search/20170404' } 5 | let(:affiliate_id) { 'dummy_affiliate_id' } 6 | let(:application_id) { 'dummy_application_id' } 7 | let(:expected_query) do 8 | { 9 | affiliateId: affiliate_id, 10 | applicationId: application_id, 11 | formatVersion: '2', 12 | keyword: 'Ruby' 13 | } 14 | end 15 | 16 | before do 17 | RakutenWebService.configure do |c| 18 | c.affiliate_id = affiliate_id 19 | c.application_id = application_id 20 | end 21 | end 22 | 23 | describe '.search' do 24 | before do 25 | response = JSON.parse(fixture('books/foreign_book_search_with_keyword_Ruby.json')) 26 | @expected_request = stub_request(:get, endpoint). 27 | with(query: expected_query).to_return(body: response.to_json) 28 | 29 | response['page'] = 2 30 | response['first'] = 31 31 | response['last'] = 60 32 | @second_request = stub_request(:get, endpoint). 33 | with(query: expected_query.merge(page: 2)). 34 | to_return(body: response.to_json) 35 | end 36 | 37 | specify 'call endpoint when accessing results' do 38 | books = RakutenWebService::Books::ForeignBook.search(keyword: 'Ruby') 39 | expect(@expected_request).to_not have_been_made 40 | 41 | books.first 42 | expect(@expected_request).to have_been_made.once 43 | end 44 | end 45 | 46 | context 'When using Books::Total.search' do 47 | let(:book) do 48 | RWS::Books::ForeignBook.new(isbn: '12345') 49 | end 50 | 51 | before do 52 | @expected_request = stub_request(:get, endpoint). 53 | with(query: { affiliateId: affiliate_id, applicationId: application_id, formatVersion: '2', isbn: '12345' }). 54 | to_return(body: { Items: [ { title: 'foo' } ] }.to_json) 55 | end 56 | 57 | specify 'retrieves automatically if accessing the value of lack attribute' do 58 | expect(book.title).to eq('foo') 59 | expect(@expected_request).to have_been_made.once 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/ichiba/tag_group_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'rakuten_web_service/ichiba/tag_group' 3 | 4 | describe RakutenWebService::Ichiba::TagGroup do 5 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/IchibaTag/Search/20140222' } 6 | let(:affiliate_id) { 'affiliate_id' } 7 | let(:application_id) { 'application_id' } 8 | let(:expected_query) do 9 | { 10 | affiliateId: affiliate_id, 11 | applicationId: application_id, 12 | formatVersion: '2', 13 | tagId: '1000317' 14 | } 15 | end 16 | 17 | before do 18 | response = JSON.parse(fixture('ichiba/tag_search.json')) 19 | @expected_request = stub_request(:get, endpoint). 20 | with(query: expected_query).to_return(body: response.to_json) 21 | 22 | RakutenWebService.configure do |c| 23 | c.affiliate_id = affiliate_id 24 | c.application_id = application_id 25 | end 26 | end 27 | 28 | describe '.search' do 29 | let(:expected_json) do 30 | response = JSON.parse(fixture('ichiba/tag_search.json')) 31 | response['tagGroups'][0] 32 | end 33 | 34 | before do 35 | @tag_group = RakutenWebService::Ichiba::TagGroup.search({tagId: '1000317'}).first 36 | end 37 | 38 | subject { @tag_group } 39 | 40 | specify 'should call the endpoint once' do 41 | expect(@expected_request).to have_been_made.once 42 | end 43 | 44 | specify 'should be access by key' do 45 | expect(subject['tagGroupId']).to eq(expected_json['tagGroupId']) 46 | expect(subject['tag_group_id']).to eq(expected_json['tagGroupId']) 47 | end 48 | end 49 | 50 | describe '#tags' do 51 | let(:response) { JSON.parse(fixture('ichiba/tag_search.json')) } 52 | let(:expected_tag) { response['tagGroups'][0]['tags'][0] } 53 | 54 | before do 55 | @tag_group = RakutenWebService::Ichiba::TagGroup.search(tagId: '1000317').first 56 | end 57 | 58 | subject { @tag_group.tags } 59 | 60 | specify 'responds Tags object' do 61 | subject.map do |tag| 62 | expect(tag.id).to eq(expected_tag['tagId']) 63 | expect(tag.name).to eq(expected_tag['tagName']) 64 | expect(tag['parentTagId']).to eq(expected_tag['parentTagId']) 65 | end 66 | end 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/gora/plan_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::Gora::Plan do 4 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/Gora/GoraPlanSearch/20170623' } 5 | let(:affiliate_id) { 'dummy_affiliate_id' } 6 | let(:application_id) { 'dummy_application_id' } 7 | let(:expected_query) do 8 | { 9 | affiliateId: affiliate_id, 10 | applicationId: application_id, 11 | formatVersion: '2', 12 | areaCode: '12,14', 13 | playDate: '2016-04-24', 14 | } 15 | end 16 | 17 | before do 18 | RakutenWebService.configure do |c| 19 | c.affiliate_id = affiliate_id 20 | c.application_id = application_id 21 | end 22 | end 23 | 24 | describe '.search' do 25 | before do 26 | response = JSON.parse(fixture('gora/plan_search_with_area_code.json')) 27 | @expected_request = stub_request(:get, endpoint). 28 | with(query: expected_query).to_return(body: response.to_json) 29 | end 30 | 31 | specify 'endpoint should not be called' do 32 | expect(@expected_request).to_not have_been_made 33 | end 34 | 35 | context 'just call the search method' do 36 | before do 37 | @plans = RakutenWebService::Gora::Plan.search(areaCode: expected_query[:areaCode], playDate: expected_query[:playDate]) 38 | end 39 | 40 | specify 'endpoint should not be called' do 41 | expect(@expected_request).to_not have_been_made 42 | end 43 | 44 | describe 'a respond object' do 45 | let(:expected_json) do 46 | response = JSON.parse(fixture('gora/plan_search_with_area_code.json')) 47 | response['Items'][0] 48 | end 49 | 50 | subject { @plans.first } 51 | 52 | it { is_expected.to be_a RakutenWebService::Gora::Plan } 53 | specify 'should be accessed by key' do 54 | expect(subject['golfCourseId']).to eq(expected_json['golfCourseId']) 55 | expect(subject['golf_course_id']).to eq(expected_json['golfCourseId']) 56 | end 57 | 58 | describe '#golf_course_id' do 59 | subject { super().golf_course_id } 60 | it { is_expected.to eq(expected_json['golfCourseId']) } 61 | end 62 | end 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/travel/simple_hotel_search_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'rakuten_web_service' 3 | 4 | describe RakutenWebService::Travel::Hotel do 5 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/Travel/SimpleHotelSearch/20170426' } 6 | let(:affiliate_id) { 'dummy_affiliate_id' } 7 | let(:application_id) { 'dummy_application_id' } 8 | let(:expected_query) do 9 | { 10 | affiliateId: affiliate_id, 11 | applicationId: application_id, 12 | formatVersion: 2, 13 | largeClassCode: 'japan', 14 | middleClassCode: 'hokkaido', 15 | smallClassCode: 'sapporo', 16 | detailClassCode: 'A' 17 | } 18 | end 19 | 20 | before do 21 | RakutenWebService.configure do |c| 22 | c.affiliate_id = affiliate_id 23 | c.application_id = application_id 24 | end 25 | end 26 | 27 | describe '.search' do 28 | let(:query) do 29 | { 30 | largeClassCode: 'japan', 31 | middleClassCode: 'hokkaido', 32 | smallClassCode: 'sapporo', 33 | detailClassCode: 'A' 34 | } 35 | end 36 | 37 | before do 38 | response = JSON.parse(fixture('travel/simple_hotel_search.json')) 39 | @expected_request = stub_request(:get, endpoint) 40 | .with(query: expected_query).to_return(body: response.to_json) 41 | 42 | response['pagingInfo']['page'] = 2 43 | response['pagingInfo']['pageCount'] = 2 44 | response['pagingInfo']['first'] = 31 45 | response['pagingInfo']['last'] = 60 46 | @second_request = stub_request(:get, endpoint) 47 | .with(query: expected_query.merge(page: 2)) 48 | .to_return(body: response.to_json) 49 | end 50 | 51 | let(:results) { RakutenWebService::Travel::Hotel.search(query) } 52 | let(:first) { results.first } 53 | 54 | specify 'responds an array of RWS::Travel::Hotel object' do 55 | expect(first).to be_a(RakutenWebService::Travel::Hotel) 56 | end 57 | specify 'respond object has 2 methods representing basic info and rating info ' do 58 | expect(first.basic_info).to eq(first['hotelBasicInfo']) 59 | expect(first.rating_info).to eq(first['hotelRatingInfo']) 60 | end 61 | specify 'should have 60 objects by getting responses automatically' do 62 | expect(results.to_a.size).to eql 30 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/ichiba/product_search_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::Ichiba::Product do 4 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/Product/Search/20170426' } 5 | let(:affiliate_id) { 'dummy_affiliate_id' } 6 | let(:application_id) { 'dummy_application_id' } 7 | let(:expected_query) do 8 | { 9 | affiliateId: affiliate_id, 10 | applicationId: application_id, 11 | formatVersion: '2', 12 | keyword: 'Ruby' 13 | } 14 | end 15 | 16 | before do 17 | RakutenWebService.configure do |c| 18 | c.affiliate_id = affiliate_id 19 | c.application_id = application_id 20 | end 21 | end 22 | 23 | describe '.search' do 24 | before do 25 | response = JSON.parse(fixture('ichiba/product_search.json')) 26 | @expected_request = stub_request(:get, endpoint). 27 | with(query: expected_query).to_return(body: response.to_json) 28 | 29 | response['page'] = 2 30 | response['first'] = 31 31 | response['last'] = 60 32 | response['pageCount'] = 2 33 | @second_request = stub_request(:get, endpoint). 34 | with(query: expected_query.merge(page: 2)). 35 | to_return(body: response.to_json) 36 | end 37 | 38 | subject { RakutenWebService::Ichiba::Product.search(keyword: 'Ruby') } 39 | 40 | specify '' do 41 | expect(subject.count).to eq(30) 42 | end 43 | 44 | describe 'For an response' do 45 | let(:expected_product) do 46 | JSON.parse(fixture('ichiba/product_search.json'))['Products'].first 47 | end 48 | let(:product) { RakutenWebService::Ichiba::Product.search(keyword: 'Ruby').first } 49 | 50 | specify 'should have methods to respond keys following with "product"' do 51 | expect(product.id).to eq(expected_product['productId']) 52 | expect(product.name).to eq(expected_product['productName']) 53 | expect(product.url_pc).to eq(expected_product['productUrlPC']) 54 | expect(product.url_mobile).to eq(expected_product['productUrlMobile']) 55 | expect(product.caption).to eq(expected_product['productCaption']) 56 | end 57 | specify 'should have genre method' do 58 | expect(RakutenWebService::Ichiba::Genre).to receive(:new).with(expected_product['genreId']) 59 | 60 | product.genre 61 | end 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/gora/course_detail_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::Gora::CourseDetail do 4 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/Gora/GoraGolfCourseDetail/20170623' } 5 | let(:affiliate_id) { 'dummy_affiliate_id' } 6 | let(:application_id) { 'dummy_application_id' } 7 | let(:expected_query) do 8 | { 9 | affiliateId: affiliate_id, 10 | applicationId: application_id, 11 | formatVersion: '2', 12 | golfCourseId: 120092 13 | } 14 | end 15 | 16 | before do 17 | RakutenWebService.configure do |c| 18 | c.affiliate_id = affiliate_id 19 | c.application_id = application_id 20 | end 21 | end 22 | 23 | describe '#find' do 24 | before do 25 | response = JSON.parse(fixture('gora/course_detail_search.json')) 26 | @expected_request = stub_request(:get, endpoint). 27 | with(query: expected_query).to_return(body: response.to_json) 28 | end 29 | 30 | context 'call the find method' do 31 | describe 'a respond object' do 32 | let(:expected_json) do 33 | response = JSON.parse(fixture('gora/course_detail_search.json')) 34 | response['Item'] 35 | end 36 | 37 | subject { RakutenWebService::Gora::CourseDetail.find(expected_query[:golfCourseId]) } 38 | 39 | it { is_expected.to be_a RakutenWebService::Gora::CourseDetail } 40 | 41 | specify 'should be accessed by key' do 42 | expect(subject['golfCourseName']).to eq(expected_json['golfCourseName']) 43 | expect(subject['golf_course_name']).to eq(expected_json['golfCourseName']) 44 | end 45 | 46 | describe '#golf_course_name' do 47 | subject { super().golf_course_name } 48 | it { is_expected.to eq(expected_json['golfCourseName']) } 49 | end 50 | 51 | specify 'should be accessed to ratings' do 52 | expect(subject.ratings.size).to eq(expected_json['ratings'].size) 53 | expect(subject.ratings.first.nick_name).to eq(expected_json['ratings'].first['nickName']) 54 | end 55 | 56 | specify 'should be accessed to new_plans' do 57 | expect(subject.new_plans.size).to eq(expected_json['newPlans'].size) 58 | expect(subject.new_plans.first.name).to eq(expected_json['newPlans'].first['planName']) 59 | end 60 | end 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/gora/course_detail.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/resource' 4 | 5 | module RakutenWebService 6 | module Gora 7 | class CourseDetail < Resource 8 | class << self 9 | def find(golf_course_id) 10 | search(golfCourseId: golf_course_id).first 11 | end 12 | end 13 | 14 | endpoint 'https://app.rakuten.co.jp/services/api/Gora/GoraGolfCourseDetail/20170623' 15 | 16 | parser do |response| 17 | [new(response['Item'])] 18 | end 19 | 20 | attribute :carrier, :golfCourseId, :golfCourseName, :golfCourseAbbr, :golfCourseNameKana, :golfCourseCaption, 21 | :information, :highway, :ic, :icDistance, :latitude, :longitude, :postalCode, :address, :telephoneNo, 22 | :faxNo, :openDay, :closeDay, :creditCard, :shoes, :dressCode, :practiceFacility, :lodgingFacility, 23 | :otherFacility, :golfCourseImageUrl1, :golfCourseImageUrl2, :golfCourseImageUrl3, :golfCourseImageUrl4, 24 | :golfCourseImageUrl5, :weekdayMinPrice, :baseWeekdayMinPrice, :holidayMinPrice, :baseHolidayMinPrice, 25 | :designer, :courseType, :courseVerticalInterval, :dimension, :green, :greenCount, :holeCount, :parCount, 26 | :courseName, :courseDistance, :longDrivingContest, :nearPin, :ratingNum, :evaluation, :staff, :facility, 27 | :meal, :course, :costperformance, :distance, :fairway, :reserveCalUrl, :voiceUrl, :layoutUrl, :routeMapUrl 28 | 29 | def ratings 30 | get_attribute('ratings').map { |rating| Rating.new(rating) } 31 | end 32 | 33 | def new_plans 34 | get_attribute('newPlans').map { |plan| Plan.new(plan) } 35 | end 36 | 37 | class Rating < Resource 38 | class << self 39 | def search(_options) 40 | raise 'There is no API endpoint for this resource.' 41 | end 42 | end 43 | attribute :title, :nickName, :prefecture, :age, :sex, :times, :evaluation, :staff, :facility, :meal, :course, 44 | :costperformance, :distance, :fairway, :comment 45 | end 46 | 47 | class Plan < Resource 48 | class << self 49 | def search(_options) 50 | raise 'There is no API endpoint for this resource.' 51 | end 52 | end 53 | attribute :month, :planName, :planDate, :service, :price, :basePrice, :salesTax, :courseUseTax, :otherTax 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/recipe_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::Recipe do 4 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/Recipe/CategoryRanking/20170426' } 5 | let(:affiliate_id) { 'dummy_affiliate_id' } 6 | let(:application_id) { 'dummy_application_id' } 7 | let(:expected_query) do 8 | { 9 | affiliateId: affiliate_id, 10 | applicationId: application_id, 11 | formatVersion: '2', 12 | categoryId: category_id 13 | } 14 | end 15 | let(:expected_query_without_category_id) do 16 | { 17 | affiliateId: affiliate_id, 18 | applicationId: application_id, 19 | formatVersion: '2' 20 | } 21 | end 22 | 23 | before do 24 | RakutenWebService.configure do |c| 25 | c.affiliate_id = affiliate_id 26 | c.application_id = application_id 27 | end 28 | end 29 | 30 | describe '.search' do 31 | it 'should not be called' do 32 | expect { RWS::Recipe.search(category_id: '10') }.to raise_error(NoMethodError) 33 | end 34 | end 35 | 36 | describe '.ranking' do 37 | context 'get ranking without category_id' do 38 | before do 39 | response = JSON.parse(fixture('recipe/ranking.json')) 40 | 41 | @expected_request = stub_request(:get, endpoint). 42 | with(query: expected_query_without_category_id). 43 | to_return(body: response.to_json) 44 | end 45 | 46 | subject { RakutenWebService::Recipe.ranking } 47 | 48 | it 'should call search without category_id' do 49 | subject.first 50 | 51 | expect(@expected_request).to have_been_made.once 52 | end 53 | it 'should return an array of Recipe' do 54 | expect(subject).to be_all { |r| r.is_a?(RWS::Recipe) } 55 | end 56 | end 57 | 58 | context 'get ranking with category_id' do 59 | let(:category_id) { '30' } 60 | 61 | before do 62 | response = JSON.parse(fixture('recipe/ranking.json')) 63 | 64 | @expected_request = stub_request(:get, endpoint). 65 | with(query: expected_query). 66 | to_return(body: response.to_json) 67 | end 68 | 69 | subject { RakutenWebService::Recipe.ranking(category_id) } 70 | 71 | it 'should call search with category_id' do 72 | subject.first 73 | 74 | expect(@expected_request).to have_been_made.once 75 | end 76 | it 'should return an array of Recipe' do 77 | expect(subject).to be_all { |r| r.is_a?(RWS::Recipe) } 78 | end 79 | end 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/kobo/ebook_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::Kobo::Ebook do 4 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/Kobo/EbookSearch/20170426' } 5 | let(:affiliate_id) { 'dummy_affiliate_id' } 6 | let(:application_id) { 'dummy_application_id' } 7 | let(:expected_query) do 8 | { 9 | affiliateId: affiliate_id, 10 | applicationId: application_id, 11 | formatVersion: '2', 12 | title: 'Ruby' 13 | } 14 | end 15 | 16 | before do 17 | RakutenWebService.configure do |c| 18 | c.affiliate_id = affiliate_id 19 | c.application_id = application_id 20 | end 21 | end 22 | 23 | describe '.search' do 24 | before do 25 | response = JSON.parse(fixture('kobo/ebook_search_with_Ruby.json')) 26 | @expected_request = stub_request(:get, endpoint). 27 | with(query: expected_query).to_return(body: response.to_json) 28 | end 29 | 30 | specify 'call endpoint when accessing results' do 31 | ebooks = RakutenWebService::Kobo::Ebook.search(title: 'Ruby') 32 | expect(@expected_request).to_not have_been_made 33 | 34 | ebook = ebooks.first 35 | expect(@expected_request).to have_been_made.once 36 | expect(ebook).to be_a(RWS::Kobo::Ebook) 37 | end 38 | end 39 | 40 | describe "#genre_information" do 41 | before do 42 | response = JSON.parse(fixture('kobo/ebook_search_with_Ruby.json')) 43 | stub_request(:get, endpoint). 44 | with(query: expected_query).to_return(body: response.to_json) 45 | end 46 | 47 | subject { RakutenWebService::Kobo::Ebook.search(title: 'Ruby') } 48 | 49 | it "returns GenreInformation object" do 50 | expect(subject.genre_information).to be_a(RakutenWebService::GenreInformation) 51 | end 52 | 53 | describe "its current attributes" do 54 | subject { super().genre_information.current } 55 | 56 | it "returns the current genre information" do 57 | expect(subject.name).to eq("電子書籍") 58 | end 59 | end 60 | end 61 | 62 | describe '#genre' do 63 | let(:response) { JSON.parse(fixture('kobo/ebook_search_with_Ruby.json')) } 64 | 65 | specify 'respond Kobo::Genre object' do 66 | stub_request(:get, endpoint).with(query: expected_query). 67 | to_return(body: response.to_json) 68 | 69 | expected_item = response['Items'][0] 70 | expect(RakutenWebService::Kobo::Genre).to receive('new').with(expected_item['koboGenreId']) 71 | 72 | RakutenWebService::Kobo::Ebook.search(title: 'Ruby').first.genre 73 | end 74 | end 75 | end 76 | -------------------------------------------------------------------------------- /spec/fixtures/kobo/genre_search.json: -------------------------------------------------------------------------------- 1 | { 2 | "current": { 3 | "koboGenreId": "101", 4 | "koboGenreName": "電子書籍", 5 | "genreLevel": 1 6 | }, 7 | "children": [ 8 | { 9 | "koboGenreId": "101901", 10 | "koboGenreName": "小説・エッセイ", 11 | "genreLevel": 2 12 | }, 13 | { 14 | "koboGenreId": "101902", 15 | "koboGenreName": "ミステリー・サスペンス", 16 | "genreLevel": 2 17 | }, 18 | { 19 | "koboGenreId": "101903", 20 | "koboGenreName": "ライトノベル", 21 | "genreLevel": 2 22 | }, 23 | { 24 | "koboGenreId": "101904", 25 | "koboGenreName": "漫画(コミック)", 26 | "genreLevel": 2 27 | }, 28 | { 29 | "koboGenreId": "101905", 30 | "koboGenreName": "ビジネス・経済・就職", 31 | "genreLevel": 2 32 | }, 33 | { 34 | "koboGenreId": "101906", 35 | "koboGenreName": "語学・学習参考書・資格", 36 | "genreLevel": 2 37 | }, 38 | { 39 | "koboGenreId": "101907", 40 | "koboGenreName": "人文・思想・社会", 41 | "genreLevel": 2 42 | }, 43 | { 44 | "koboGenreId": "101908", 45 | "koboGenreName": "科学・医学・技術", 46 | "genreLevel": 2 47 | }, 48 | { 49 | "koboGenreId": "101909", 50 | "koboGenreName": "美容・暮らし・健康・料理", 51 | "genreLevel": 2 52 | }, 53 | { 54 | "koboGenreId": "101910", 55 | "koboGenreName": "ホビー・スポーツ・美術", 56 | "genreLevel": 2 57 | }, 58 | { 59 | "koboGenreId": "101911", 60 | "koboGenreName": "旅行・留学・アウトドア", 61 | "genreLevel": 2 62 | }, 63 | { 64 | "koboGenreId": "101912", 65 | "koboGenreName": "PC・システム開発", 66 | "genreLevel": 2 67 | }, 68 | { 69 | "koboGenreId": "101913", 70 | "koboGenreName": "エンターテインメント", 71 | "genreLevel": 2 72 | }, 73 | { 74 | "koboGenreId": "101914", 75 | "koboGenreName": "ノンフィクション", 76 | "genreLevel": 2 77 | }, 78 | { 79 | "koboGenreId": "101915", 80 | "koboGenreName": "写真集", 81 | "genreLevel": 2 82 | }, 83 | { 84 | "koboGenreId": "101916", 85 | "koboGenreName": "絵本・児童書", 86 | "genreLevel": 2 87 | }, 88 | { 89 | "koboGenreId": "101940", 90 | "koboGenreName": "ボーイズラブ(BL)・ティーンズラブ(TL)", 91 | "genreLevel": 2 92 | }, 93 | { 94 | "koboGenreId": "101950", 95 | "koboGenreName": "雑誌", 96 | "genreLevel": 2 97 | }, 98 | { 99 | "koboGenreId": "101970", 100 | "koboGenreName": "洋書", 101 | "genreLevel": 2 102 | } 103 | ], 104 | "parents": [] 105 | } 106 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/configuration_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'rakuten_web_service/configuration' 3 | 4 | describe RakutenWebService::Configuration do 5 | describe '#initialize' do 6 | context "environment variable RWS_APPLICATION_ID and RWS_AFFILIATE_ID are defined" do 7 | before do 8 | ENV['RWS_APPLICATION_ID'] = 'env_application_id' 9 | ENV['RWS_AFFILIATE_ID'] = 'env_affiliate_id' 10 | end 11 | 12 | after do 13 | ENV.delete 'RWS_APPLICATION_ID' 14 | ENV.delete 'RWS_AFFILIATE_ID' 15 | end 16 | 17 | subject { RakutenWebService::Configuration.new } 18 | 19 | specify "the application id is set by the environment variable" do 20 | expect(subject.application_id).to eq 'env_application_id' 21 | expect(subject.affiliate_id).to eq 'env_affiliate_id' 22 | end 23 | end 24 | end 25 | 26 | describe '#debug_mode?' do 27 | let(:configuration) { RakutenWebService.configuration } 28 | 29 | before do 30 | configuration.debug = true 31 | end 32 | 33 | it 'should return true' do 34 | expect(configuration).to be_debug_mode 35 | end 36 | 37 | context 'When RWS_SDK_DEBUG is defined' do 38 | before do 39 | ENV['RWS_SDK_DEBUG'] = 'true' 40 | configuration.debug = false 41 | end 42 | 43 | after do 44 | ENV.delete('RWS_SDK_DEBUG') 45 | end 46 | 47 | it 'should return true' do 48 | expect(configuration).to be_debug_mode 49 | expect(configuration.debug).to be_falsey 50 | end 51 | end 52 | end 53 | 54 | describe "#default_parameters" do 55 | before do 56 | RakutenWebService.configure do |c| 57 | c.application_id = application_id 58 | end 59 | end 60 | 61 | context "When application id is given" do 62 | let(:application_id) { 'app_id' } 63 | 64 | subject { RakutenWebService.configuration.default_parameters } 65 | 66 | it "has application_id key and its value is a given value" do 67 | expect(subject[:application_id]).to eq 'app_id' 68 | end 69 | end 70 | context "When application id is not given" do 71 | let(:application_id) { nil } 72 | 73 | it "raises an error" do 74 | expect { 75 | RakutenWebService.configuration.default_parameters 76 | }.to raise_error(RuntimeError, "Application ID is not defined") 77 | end 78 | end 79 | context "When application id is an empty string" do 80 | let(:application_id) { '' } 81 | 82 | it "raises an error" do 83 | expect { 84 | RakutenWebService.configuration.default_parameters 85 | }.to raise_error(RuntimeError, "Application ID is not defined") 86 | end 87 | end 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/search_result.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/all_proxy' 4 | require 'rakuten_web_service/genre_information' 5 | require 'rakuten_web_service/string_support' 6 | 7 | module RakutenWebService 8 | class SearchResult 9 | include Enumerable 10 | 11 | using RakutenWebService::StringSupport 12 | 13 | def initialize(params, resource_class) 14 | @params = params.dup 15 | @resource_class = resource_class 16 | @client = RakutenWebService::Client.new(resource_class) 17 | end 18 | 19 | def search(params) 20 | self.class.new(self.params.dup.merge!(params), @resource_class) 21 | end 22 | alias with search 23 | 24 | def each 25 | response.each do |resource| 26 | yield resource 27 | end 28 | end 29 | 30 | def all(&block) 31 | proxy = AllProxy.new(self) 32 | proxy.each(&block) if block 33 | proxy 34 | end 35 | 36 | def params 37 | @params ||= {} 38 | end 39 | 40 | def params_to_get_next_page 41 | @params.merge('page' => @response.body['page'] + 1) 42 | end 43 | 44 | def order(options) 45 | new_params = params.dup 46 | if options.to_s == 'standard' 47 | new_params[:sort] = 'standard' 48 | return self.class.new(new_params, @resource_class) 49 | end 50 | unless options.is_a? Hash 51 | raise ArgumentError, "Invalid Sort Option: #{options.inspect}" 52 | end 53 | 54 | key, sort_order = *options.to_a.last 55 | case sort_order.to_s.downcase 56 | when 'desc' then new_params[:sort] = "-#{key}".to_camel 57 | when 'asc' then new_params[:sort] = "+#{key}".to_camel 58 | end 59 | self.class.new(new_params, @resource_class) 60 | end 61 | 62 | def query 63 | ensure_retries { @client.get(params) } 64 | end 65 | alias fetch_result query 66 | 67 | def response 68 | @response ||= query 69 | end 70 | 71 | def next_page? 72 | response.next_page? 73 | end 74 | 75 | def next_page 76 | search(page: response.page + 1) 77 | end 78 | 79 | def previous_page? 80 | response.previous_page? 81 | end 82 | 83 | def previous_page 84 | search(page: response.page - 1) 85 | end 86 | 87 | def page(num) 88 | search(page: num) 89 | end 90 | 91 | def genre_information 92 | response.genre_information 93 | end 94 | 95 | private 96 | 97 | def ensure_retries(max_retries = RakutenWebService.configuration.max_retries) 98 | yield 99 | rescue RWS::TooManyRequests => e 100 | raise e if max_retries <= 0 101 | max_retries -= 1 102 | sleep 1 103 | retry 104 | end 105 | end 106 | end 107 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "command": "${workspaceRoot}/bin/rake", 6 | "tasks": [ 7 | { 8 | "label": "build", 9 | "type": "shell", 10 | "command": "${workspaceRoot}/bin/rake", 11 | "args": [ 12 | "build" 13 | ], 14 | "problemMatcher": [], 15 | "group": { 16 | "_id": "build", 17 | "isDefault": false 18 | } 19 | }, 20 | { 21 | "label": "release", 22 | "type": "shell", 23 | "command": "${workspaceRoot}/bin/rake", 24 | "args": [ 25 | "release", 26 | "--trace" 27 | ], 28 | "problemMatcher": [] 29 | }, 30 | { 31 | "label": "spec", 32 | "type": "shell", 33 | "command": "${workspaceRoot}/bin/rake", 34 | "args": [ 35 | "spec" 36 | ], 37 | "problemMatcher": { 38 | "owner": "ruby", 39 | "fileLocation": [ 40 | "relative", 41 | "${workspaceRoot}" 42 | ], 43 | "pattern": { 44 | "regexp": "^rspec\\s+([\\./\\w]+):(\\d+)\\s+#\\s+(.+)$", 45 | "file": 1, 46 | "line": 2, 47 | "message": 3 48 | } 49 | }, 50 | "group": { 51 | "_id": "test", 52 | "isDefault": false 53 | } 54 | }, 55 | { 56 | "label": "Nearest Spec", 57 | "type": "shell", 58 | "command": "${workspaceRoot}/bin/rspec", 59 | "args": [ 60 | "${file}:${lineNumber}" 61 | ], 62 | "problemMatcher": [] 63 | }, 64 | { 65 | "label": "Current Spec", 66 | "type": "shell", 67 | "command": "${workspaceRoot}/bin/rspec", 68 | "args": [ 69 | "${file}" 70 | ], 71 | "problemMatcher": { 72 | "owner": "ruby", 73 | "fileLocation": [ 74 | "relative", 75 | "${workspaceRoot}" 76 | ], 77 | "pattern": { 78 | "regexp": "^rspec\\s+([\\./\\w]+):(\\d+)\\s+#\\s+(.+)$", 79 | "file": 1, 80 | "line": 2, 81 | "message": 3 82 | } 83 | } 84 | }, 85 | { 86 | "label": "bunlde install", 87 | "type": "shell", 88 | "command": "bundle", 89 | "args": [ 90 | "install" 91 | ], 92 | "problemMatcher": [] 93 | } 94 | ] 95 | } -------------------------------------------------------------------------------- /lib/rakuten_web_service/resource.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/client' 4 | require 'rakuten_web_service/search_result' 5 | 6 | require 'rakuten_web_service/string_support' 7 | 8 | module RakutenWebService 9 | class Resource 10 | attr_reader :params 11 | 12 | using RakutenWebService::StringSupport 13 | 14 | class << self 15 | attr_writer :resource_name 16 | 17 | def inherited(subclass) 18 | @@subclasses ||= [] 19 | @@subclasses.push(subclass) 20 | end 21 | 22 | def subclasses 23 | @@subclasses || [] 24 | end 25 | 26 | def attribute(*attribute_names) 27 | attribute_names.each do |attribute_name| 28 | attribute_name = attribute_name.to_s 29 | 30 | define_getter_for_attribute(attribute_name) 31 | next unless attribute_name.end_with?('Flag') 32 | 33 | define_asking_method_for_attribute(attribute_name) 34 | end 35 | end 36 | 37 | def search(options) 38 | SearchResult.new(options, self) 39 | end 40 | 41 | def all(options, &block) 42 | if block 43 | search(options).all(&block) 44 | else 45 | search(options).all 46 | end 47 | end 48 | 49 | def resource_name 50 | @resource_name ||= name.split('::').last.downcase 51 | end 52 | 53 | def endpoint(url = nil) 54 | @endpoint = url || @endpoint 55 | end 56 | 57 | def parser(&block) 58 | instance_eval do 59 | define_singleton_method :parse_response, block 60 | end 61 | end 62 | 63 | private 64 | 65 | def define_getter_for_attribute(attribute_name) 66 | method_name = attribute_name.to_snake 67 | method_name.sub!(/^#{resource_name}_(\w+)$/, '\1') 68 | 69 | define_method method_name do 70 | get_attribute(attribute_name) 71 | end 72 | end 73 | 74 | def define_asking_method_for_attribute(attribute_name) 75 | method_name = attribute_name.to_snake 76 | method_name.sub!(/^#{resource_name}_(\w+)$/, '\1') 77 | method_name.sub!(/(.+)_flag$/, '\1') 78 | 79 | define_method "#{method_name}?" do 80 | get_attribute(attribute_name) == 1 81 | end 82 | end 83 | end 84 | 85 | def initialize(params) 86 | @params = {} 87 | params.each { |k, v| @params[k.to_s] = v } 88 | end 89 | 90 | def [](key) 91 | camel_key = key.to_camel 92 | @params[key] || @params[camel_key] 93 | end 94 | 95 | def get_attribute(name) 96 | @params[name.to_s] 97 | end 98 | 99 | def attributes 100 | params.keys 101 | end 102 | 103 | def ==(other) 104 | raise ArgumentError unless other.is_a?(RakutenWebService::Resource) 105 | 106 | params.keys.all? do |k| 107 | @params[k] == other.params[k] 108 | end 109 | end 110 | end 111 | end 112 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/gora/course_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::Gora::Course do 4 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/Gora/GoraGolfCourseSearch/20170623' } 5 | let(:affiliate_id) { 'dummy_affiliate_id' } 6 | let(:application_id) { 'dummy_application_id' } 7 | let(:expected_query) do 8 | { 9 | affiliateId: affiliate_id, 10 | applicationId: application_id, 11 | formatVersion: '2', 12 | keyword: '軽井沢' 13 | } 14 | end 15 | 16 | before do 17 | RakutenWebService.configure do |c| 18 | c.affiliate_id = affiliate_id 19 | c.application_id = application_id 20 | end 21 | end 22 | 23 | describe '.search' do 24 | before do 25 | response = JSON.parse(fixture('gora/course_search_with_Karuizawa.json')) 26 | response['pageCount'] = 2 27 | @expected_request = stub_request(:get, endpoint). 28 | with(query: expected_query).to_return(body: response.to_json) 29 | 30 | response['page'] = 2 31 | response['first'] = 31 32 | response['last'] = 60 33 | @second_request = stub_request(:get, endpoint). 34 | with(query: expected_query.merge(page: 2)). 35 | to_return(body: response.to_json) 36 | end 37 | 38 | context 'just call the search method' do 39 | before do 40 | @courses = RakutenWebService::Gora::Course.search(keyword: '軽井沢') 41 | end 42 | 43 | specify 'endpoint should not be called' do 44 | expect(@expected_request).to_not have_been_made 45 | end 46 | 47 | describe 'a respond object' do 48 | let(:expected_json) do 49 | response = JSON.parse(fixture('gora/course_search_with_Karuizawa.json')) 50 | response['Items'][0] 51 | end 52 | 53 | subject { @courses.first } 54 | 55 | it { is_expected.to be_a RakutenWebService::Gora::Course } 56 | specify 'should be accessed by key' do 57 | expect(subject['golfCourseName']).to eq(expected_json['golfCourseName']) 58 | expect(subject['golf_course_name']).to eq(expected_json['golfCourseName']) 59 | end 60 | 61 | describe '#golf_course_name' do 62 | subject { super().golf_course_name } 63 | it { is_expected.to eq(expected_json['golfCourseName']) } 64 | end 65 | end 66 | 67 | context 'after that, call each' do 68 | before do 69 | @courses.each { |i| i } 70 | end 71 | 72 | specify 'endpoint should be called' do 73 | expect(@expected_request).to have_been_made.once 74 | expect(@second_request).not_to have_been_made.once 75 | end 76 | end 77 | 78 | context 'after that, call fetch_result' do 79 | before do 80 | @courses.fetch_result 81 | end 82 | 83 | specify 'endpoint should be called' do 84 | expect(@expected_request).to have_been_made.once 85 | expect(@second_request).to_not have_been_made.once 86 | end 87 | end 88 | 89 | describe '#all' do 90 | before do 91 | @courses.all 92 | end 93 | 94 | specify 'endpoint should not be called' do 95 | expect(@expected_request).to_not have_been_made.once 96 | expect(@second_request).to_not have_been_made.once 97 | end 98 | 99 | context 'call an enumerable method like each' do 100 | before do 101 | @courses.all.each { |i| i.to_s } 102 | end 103 | 104 | specify 'endpoint should be called' do 105 | expect(@expected_request).to have_been_made.once 106 | expect(@second_request).to have_been_made.once 107 | end 108 | end 109 | end 110 | end 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /lib/rakuten_web_service/travel/area_class.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rakuten_web_service/travel/resource' 4 | 5 | module RakutenWebService 6 | module Travel 7 | module AreaClass 8 | def search(options = {}) 9 | Base.search(options) 10 | end 11 | 12 | def [](class_code) 13 | LargeClass[class_code] || 14 | MiddleClass[class_code] || 15 | SmallClass[class_code] || 16 | DetailClass[class_code] 17 | end 18 | module_function :search, :[] 19 | 20 | class Base < RakutenWebService::Travel::Resource 21 | endpoint 'https://app.rakuten.co.jp/services/api/Travel/GetAreaClass/20131024' 22 | 23 | parser do |response| 24 | response['areaClasses']['largeClasses'].map do |data| 25 | LargeClass.new(data) 26 | end 27 | end 28 | 29 | class << self 30 | def area_classes 31 | @@area_classes ||= {} 32 | end 33 | 34 | def inherited(klass) 35 | class_name = klass.to_s.split('::').last 36 | area_level = class_name.to_s[/\A(\w*)Class\Z/, 1].downcase 37 | class << klass 38 | attr_reader :area_level 39 | end 40 | klass.instance_variable_set(:@area_level, area_level) 41 | 42 | area_classes[klass.area_level] = klass 43 | 44 | klass.attribute :"#{area_level}ClassCode", :"#{area_level}ClassName" 45 | end 46 | 47 | def [](area_code) 48 | AreaClass.search.first unless repository[area_level][area_code] 49 | repository[area_level][area_code] 50 | end 51 | 52 | def new(*args) 53 | obj = super 54 | repository[area_level][obj.class_code] = obj 55 | obj 56 | end 57 | 58 | def repository 59 | @@repository ||= Hash.new { |h, k| h[k] = {} } 60 | end 61 | end 62 | 63 | attr_reader :parent, :children 64 | 65 | def initialize(class_data, parent = nil) 66 | @parent = parent 67 | case class_data 68 | when Array 69 | @params = class_data.shift 70 | @children = class_data.shift 71 | when Hash 72 | @params = class_data 73 | @children = nil 74 | end 75 | parse_children_attributes 76 | end 77 | 78 | def area_level 79 | self.class.area_level 80 | end 81 | 82 | def class_code 83 | self["#{area_level}ClassCode"] 84 | end 85 | 86 | def class_name 87 | self["#{area_level}ClassName"] 88 | end 89 | 90 | def to_query 91 | query = { "#{area_level}ClassCode" => class_code } 92 | query = query.merge(parent.to_query) unless parent.nil? 93 | query 94 | end 95 | 96 | def search(params = {}) 97 | params = to_query.merge(params) 98 | 99 | Hotel.search(params) 100 | end 101 | 102 | private 103 | 104 | def parse_children_attributes 105 | return @children = [] if children.nil? || children.empty? 106 | 107 | children_class = children.keys.first[/\A(\w*)Classes\Z/, 1] 108 | class_name = "#{children_class}Classes" 109 | params[class_name] = children[class_name].map do |child_data| 110 | Base.area_classes[children_class].new(child_data["#{children_class}Class"], self) 111 | end 112 | @children = params[class_name] 113 | end 114 | end 115 | 116 | class LargeClass < Base 117 | end 118 | 119 | class MiddleClass < Base 120 | end 121 | 122 | class SmallClass < Base 123 | end 124 | 125 | class DetailClass < Base 126 | end 127 | end 128 | end 129 | end 130 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/genre_information_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::GenreInformation do 4 | describe '#initialize' do 5 | subject { RakutenWebService::GenreInformation.new(expected_params, RWS::Ichiba::Genre) } 6 | 7 | context "When given params only has children genres" do 8 | let(:expected_params) do 9 | { 10 | "parent" => [], 11 | "current" => [], 12 | "children" => [ 13 | { 14 | "child" => { 15 | "genreId" => "564500", 16 | "genreName" => "光回線・モバイル通信", 17 | "itemCount" => "5", 18 | "genreLevel" => "1" 19 | } 20 | } 21 | ] 22 | } 23 | end 24 | 25 | it { is_expected.to be_a(RakutenWebService::GenreInformation) } 26 | specify "its parent should be nil" do 27 | expect(subject.parent).to be_nil 28 | end 29 | specify "its current should be nil" do 30 | expect(subject.current).to be_nil 31 | end 32 | specify "its children should returns a array of Genre" do 33 | expect(subject.children).to_not be_empty 34 | end 35 | 36 | describe "each child" do 37 | subject { super().children.first } 38 | 39 | it "has genre id" do 40 | expect(subject.id).to eq('564500') 41 | end 42 | it "has genre name" do 43 | expect(subject.name).to eq('光回線・モバイル通信') 44 | end 45 | it "has its genre level" do 46 | expect(subject.level).to eq('1') 47 | end 48 | it "has its item count" do 49 | expect(subject.item_count).to eq('5') 50 | end 51 | end 52 | end 53 | context "When given params has current genre" do 54 | let(:expected_params) do 55 | { 56 | "parent" => [], 57 | "current" => [ 58 | { 59 | "genreId" => "564500", 60 | "genreName" => "スマートフォン・タブレット", 61 | "itemCount" => "313045", 62 | "genreLevel" => "1" 63 | } 64 | ], 65 | "children" => [ 66 | ] 67 | } 68 | end 69 | specify "its parent should be nil" do expect(subject.parent).to be_nil 70 | end 71 | specify "its current should be a Genre object" do 72 | expect(subject.current).to be_a(RWS::Ichiba::Genre) 73 | end 74 | specify "its children should be empty" do 75 | expect(subject.children).to be_empty 76 | end 77 | end 78 | context "When given params has parent genre" do 79 | let(:expected_params) do 80 | { 81 | "parent" => [ 82 | { 83 | "genreId" => "564500", 84 | "genreName" => "スマートフォン・タブレット", 85 | "itemCount" => "313045", 86 | "genreLevel" => "1" 87 | } 88 | ], 89 | "current" => [ 90 | { 91 | "genreId" => "560029", 92 | "genreName" => "タブレットPC本体", 93 | "itemCount" => "0", 94 | "genreLevel" => "2" 95 | } 96 | ], 97 | "children" => [ 98 | ] 99 | } 100 | end 101 | 102 | specify "its parent should be a Genre object" do 103 | expect(subject.parent).to be_a(RWS::Ichiba::Genre) 104 | end 105 | specify "its current should be a Gere object" do 106 | expect(subject.current).to be_a(RWS::Ichiba::Genre) 107 | end 108 | specify "its children should be empty" do 109 | expect(subject.children).to be_empty 110 | end 111 | 112 | context "After re-initialize Genre with same genre id" do 113 | let(:genre) { RWS::Ichiba::Genre.new('560029') } 114 | 115 | before do 116 | subject.current 117 | end 118 | 119 | it "doesn't have item_count value" do 120 | expect(genre.item_count).to be_nil 121 | end 122 | end 123 | end 124 | end 125 | end 126 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/books/book_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::Books::Book do 4 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/BooksBook/Search/20170404' } 5 | let(:affiliate_id) { 'dummy_affiliate_id' } 6 | let(:application_id) { 'dummy_application_id' } 7 | let(:expected_query) do 8 | { 9 | affiliateId: affiliate_id, 10 | applicationId: application_id, 11 | formatVersion: '2', 12 | title: 'Ruby' 13 | } 14 | end 15 | 16 | before do 17 | RakutenWebService.configure do |c| 18 | c.affiliate_id = affiliate_id 19 | c.application_id = application_id 20 | end 21 | end 22 | 23 | describe '.search' do 24 | before do 25 | response = JSON.parse(fixture('books/book_search_with_keyword_Ruby.json')) 26 | @expected_request = stub_request(:get, endpoint). 27 | with(query: expected_query).to_return(body: response.to_json) 28 | 29 | response['page'] = 2 30 | response['first'] = 31 31 | response['last'] = 60 32 | @second_request = stub_request(:get, endpoint). 33 | with(query: expected_query.merge(page: 2)). 34 | to_return(body: response.to_json) 35 | end 36 | 37 | specify 'call endpoint when accessing results' do 38 | books = RakutenWebService::Books::Book.search(title: 'Ruby') 39 | expect(@expected_request).to_not have_been_made 40 | 41 | books.first 42 | expect(@expected_request).to have_been_made.once 43 | end 44 | end 45 | 46 | describe "#genre_information" do 47 | before do 48 | response = JSON.parse(fixture('books/book_search_with_keyword_Ruby.json')) 49 | @expected_request = stub_request(:get, endpoint). 50 | with(query: expected_query).to_return(body: response.to_json) 51 | end 52 | 53 | subject { RakutenWebService::Books::Book.search(title: 'Ruby') } 54 | 55 | it "responds GenreInformation object" do 56 | expect(subject.genre_information).to be_a(RakutenWebService::GenreInformation) 57 | end 58 | 59 | 60 | describe "its attributes" do 61 | subject { super().genre_information } 62 | 63 | it "have current genre" do 64 | expect(subject.current).to be_a(RakutenWebService::Books::Genre) 65 | expect(subject.current.item_count).to eq('119') 66 | end 67 | end 68 | end 69 | 70 | describe '#genre' do 71 | let(:response) { JSON.parse(fixture('books/book_search_with_keyword_Ruby.json')) } 72 | 73 | before do 74 | res = response.dup 75 | res['page'] = 1 76 | res['first'] = 1 77 | res['last'] = 30 78 | 79 | stub_request(:get, endpoint). 80 | with(query: expected_query).to_return(body: res.to_json) 81 | end 82 | 83 | specify 'genretes RWS::Books::Genre object' do 84 | book = RWS::Books::Book.search(title: 'Ruby').first 85 | 86 | expect(RWS::Books::Genre).to receive(:new).with(response['Items'][0]['booksGenreId']) 87 | expect(book.genre).to be_a(Array) 88 | end 89 | 90 | specify 'generate an array of Books::Genre object if books_genre_id includes 2 ids' do 91 | genre_id = '001004008007/001021001010' 92 | book = RWS::Books::Book.new(booksGenreId: genre_id) 93 | genre_id.split('/').each do |id| 94 | expect(RWS::Books::Genre).to receive(:new).with(id) 95 | end 96 | 97 | expect(book.genres).to be_a(Array) 98 | end 99 | end 100 | 101 | context 'When using Books::Total.search' do 102 | let(:book) do 103 | RWS::Books::Book.new(isbn: '12345') 104 | end 105 | 106 | before do 107 | @expected_request = stub_request(:get, endpoint). 108 | with(query: { affiliateId: affiliate_id, applicationId: application_id, formatVersion: '2', isbn: '12345' }). 109 | to_return(body: { Items: [ { title: 'foo' } ] }.to_json) 110 | end 111 | 112 | specify 'retrieves automatically if accessing the value of lack attribute' do 113 | expect(book.title).to eq('foo') 114 | expect(@expected_request).to have_been_made.once 115 | end 116 | end 117 | end 118 | -------------------------------------------------------------------------------- /spec/fixtures/books/game_search_with_keyword_Ruby.json: -------------------------------------------------------------------------------- 1 | { 2 | "count": 1, 3 | "page": 1, 4 | "first": 1, 5 | "last": 1, 6 | "hits": 1, 7 | "carrier": 0, 8 | "pageCount": 1, 9 | "Items": [ 10 | { 11 | "title": "ポケットモンスター オメガルビー トランプ", 12 | "titleKana": "ポケットモンスター オメガルビー トランプ", 13 | "hardware": "玩具", 14 | "label": "任天堂", 15 | "jan": "4902370528268", 16 | "makerCode": "TRP-Z-NPOR", 17 | "itemCaption": "『ポケットモンスター オメガルビー・アルファサファイア』のポケモンたちがトランプで登場\n\n●それぞれのバージョンによって、登場するポケモンが一部異なります。\n●2種類のJOKERは「オメガルビー」と「アルファサファイア」どちらも同じものが入っています。\n\n\n\n©2015 Pokémon. ©1995-2015 Nintendo / Creatures Inc. / GAME FREAK inc.\nポケットモンスター・ポケモン・Pokémonは任天堂・クリーチャーズ・ゲームフリークの登録商標です。", 18 | "salesDate": "2015年03月08日", 19 | "itemPrice": 540, 20 | "listPrice": 0, 21 | "discountRate": 0, 22 | "discountPrice": 0, 23 | "itemUrl": "http://books.rakuten.co.jp/rb/13101447/", 24 | "affiliateUrl": "", 25 | "smallImageUrl": "http://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/8268/4902370528268.jpg?_ex=64x64", 26 | "mediumImageUrl": "http://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/8268/4902370528268.jpg?_ex=120x120", 27 | "largeImageUrl": "http://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/8268/4902370528268.jpg?_ex=200x200", 28 | "availability": "1", 29 | "postageFlag": 0, 30 | "limitedFlag": 0, 31 | "reviewCount": 0, 32 | "reviewAverage": "0.0", 33 | "booksGenreId": "006510006001" 34 | } 35 | ], 36 | "GenreInformation": [ 37 | { 38 | "current": [ 39 | { 40 | "booksGenreId": "006", 41 | "booksGenreName": "ゲーム", 42 | "itemCount": "1", 43 | "genreLevel": "1" 44 | } 45 | ], 46 | "children": [ 47 | { 48 | "booksGenreId": "006501", 49 | "booksGenreName": "PS3", 50 | "itemCount": "0", 51 | "genreLevel": "2" 52 | }, 53 | { 54 | "booksGenreId": "006502", 55 | "booksGenreName": "PSP", 56 | "itemCount": "0", 57 | "genreLevel": "2" 58 | }, 59 | { 60 | "booksGenreId": "006503", 61 | "booksGenreName": "Wii", 62 | "itemCount": "0", 63 | "genreLevel": "2" 64 | }, 65 | { 66 | "booksGenreId": "006504", 67 | "booksGenreName": "ニンテンドーDS", 68 | "itemCount": "0", 69 | "genreLevel": "2" 70 | }, 71 | { 72 | "booksGenreId": "006505", 73 | "booksGenreName": "その他", 74 | "itemCount": "0", 75 | "genreLevel": "2" 76 | }, 77 | { 78 | "booksGenreId": "006506", 79 | "booksGenreName": "PS2", 80 | "itemCount": "0", 81 | "genreLevel": "2" 82 | }, 83 | { 84 | "booksGenreId": "006507", 85 | "booksGenreName": "Xbox 360", 86 | "itemCount": "0", 87 | "genreLevel": "2" 88 | }, 89 | { 90 | "booksGenreId": "006508", 91 | "booksGenreName": "ニンテンドー3DS", 92 | "itemCount": "0", 93 | "genreLevel": "2" 94 | }, 95 | { 96 | "booksGenreId": "006509", 97 | "booksGenreName": "PS Vita", 98 | "itemCount": "0", 99 | "genreLevel": "2" 100 | }, 101 | { 102 | "booksGenreId": "006510", 103 | "booksGenreName": "おもちゃ", 104 | "itemCount": "1", 105 | "genreLevel": "2" 106 | }, 107 | { 108 | "booksGenreId": "006511", 109 | "booksGenreName": "Wii U", 110 | "itemCount": "0", 111 | "genreLevel": "2" 112 | }, 113 | { 114 | "booksGenreId": "006512", 115 | "booksGenreName": "Xbox One", 116 | "itemCount": "0", 117 | "genreLevel": "2" 118 | }, 119 | { 120 | "booksGenreId": "006513", 121 | "booksGenreName": "PS4", 122 | "itemCount": "0", 123 | "genreLevel": "2" 124 | } 125 | ] 126 | } 127 | ] 128 | } 129 | -------------------------------------------------------------------------------- /spec/fixtures/ichiba/genre_search.json: -------------------------------------------------------------------------------- 1 | { 2 | "parents": [], 3 | "current": { 4 | "genreId": 0, 5 | "genreName": "", 6 | "genreLevel": 0 7 | }, 8 | "brothers": [], 9 | "children": [ 10 | { 11 | "genreId": 101240, 12 | "genreName": "CD・DVD・楽器", 13 | "genreLevel": 1 14 | }, 15 | { 16 | "genreId": 100804, 17 | "genreName": "インテリア・寝具・収納", 18 | "genreLevel": 1 19 | }, 20 | { 21 | "genreId": 101164, 22 | "genreName": "おもちゃ・ホビー・ゲーム", 23 | "genreLevel": 1 24 | }, 25 | { 26 | "genreId": 100533, 27 | "genreName": "キッズ・ベビー・マタニティ", 28 | "genreLevel": 1 29 | }, 30 | { 31 | "genreId": 215783, 32 | "genreName": "日用品雑貨・文房具・手芸", 33 | "genreLevel": 1 34 | }, 35 | { 36 | "genreId": 216129, 37 | "genreName": "ジュエリー・アクセサリー", 38 | "genreLevel": 1 39 | }, 40 | { 41 | "genreId": 101070, 42 | "genreName": "スポーツ・アウトドア", 43 | "genreLevel": 1 44 | }, 45 | { 46 | "genreId": 100938, 47 | "genreName": "ダイエット・健康", 48 | "genreLevel": 1 49 | }, 50 | { 51 | "genreId": 100316, 52 | "genreName": "水・ソフトドリンク", 53 | "genreLevel": 1 54 | }, 55 | { 56 | "genreId": 100026, 57 | "genreName": "パソコン・周辺機器", 58 | "genreLevel": 1 59 | }, 60 | { 61 | "genreId": 216131, 62 | "genreName": "バッグ・小物・ブランド雑貨", 63 | "genreLevel": 1 64 | }, 65 | { 66 | "genreId": 100371, 67 | "genreName": "レディースファッション", 68 | "genreLevel": 1 69 | }, 70 | { 71 | "genreId": 100005, 72 | "genreName": "花・ガーデン・DIY", 73 | "genreLevel": 1 74 | }, 75 | { 76 | "genreId": 101213, 77 | "genreName": "ペット・ペットグッズ", 78 | "genreLevel": 1 79 | }, 80 | { 81 | "genreId": 211742, 82 | "genreName": "TV・オーディオ・カメラ", 83 | "genreLevel": 1 84 | }, 85 | { 86 | "genreId": 101114, 87 | "genreName": "車・バイク", 88 | "genreLevel": 1 89 | }, 90 | { 91 | "genreId": 100227, 92 | "genreName": "食品", 93 | "genreLevel": 1 94 | }, 95 | { 96 | "genreId": 100939, 97 | "genreName": "美容・コスメ・香水", 98 | "genreLevel": 1 99 | }, 100 | { 101 | "genreId": 200162, 102 | "genreName": "本・雑誌・コミック", 103 | "genreLevel": 1 104 | }, 105 | { 106 | "genreId": 101381, 107 | "genreName": "旅行・出張・チケット", 108 | "genreLevel": 1 109 | }, 110 | { 111 | "genreId": 101438, 112 | "genreName": "学び・サービス・保険", 113 | "genreLevel": 1 114 | }, 115 | { 116 | "genreId": 100000, 117 | "genreName": "百貨店・総合通販・ギフト", 118 | "genreLevel": 1 119 | }, 120 | { 121 | "genreId": 402853, 122 | "genreName": "デジタルコンテンツ", 123 | "genreLevel": 1 124 | }, 125 | { 126 | "genreId": 503190, 127 | "genreName": "車用品・バイク用品", 128 | "genreLevel": 1 129 | }, 130 | { 131 | "genreId": 100433, 132 | "genreName": "インナー・下着・ナイトウエア", 133 | "genreLevel": 1 134 | }, 135 | { 136 | "genreId": 510901, 137 | "genreName": "日本酒・焼酎", 138 | "genreLevel": 1 139 | }, 140 | { 141 | "genreId": 510915, 142 | "genreName": "ビール・洋酒", 143 | "genreLevel": 1 144 | }, 145 | { 146 | "genreId": 551167, 147 | "genreName": "スイーツ・お菓子", 148 | "genreLevel": 1 149 | }, 150 | { 151 | "genreId": 551169, 152 | "genreName": "医薬品・コンタクト・介護", 153 | "genreLevel": 1 154 | }, 155 | { 156 | "genreId": 551177, 157 | "genreName": "メンズファッション", 158 | "genreLevel": 1 159 | }, 160 | { 161 | "genreId": 558885, 162 | "genreName": "靴", 163 | "genreLevel": 1 164 | }, 165 | { 166 | "genreId": 558929, 167 | "genreName": "腕時計", 168 | "genreLevel": 1 169 | }, 170 | { 171 | "genreId": 558944, 172 | "genreName": "キッチン用品・食器・調理器具", 173 | "genreLevel": 1 174 | }, 175 | { 176 | "genreId": 562637, 177 | "genreName": "家電", 178 | "genreLevel": 1 179 | }, 180 | { 181 | "genreId": 564500, 182 | "genreName": "スマートフォン・タブレット", 183 | "genreLevel": 1 184 | }, 185 | { 186 | "genreId": 565004, 187 | "genreName": "光回線・モバイル通信", 188 | "genreLevel": 1 189 | } 190 | ], 191 | "tagGroups": [] 192 | } 193 | -------------------------------------------------------------------------------- /spec/fixtures/recipe/ranking.json: -------------------------------------------------------------------------------- 1 | { 2 | "result": [ 3 | { 4 | "recipeId": 1720001297, 5 | "recipeTitle": "マヨった時は、フライパン1つの「マヨチキン」", 6 | "recipeUrl": "http://recipe.rakuten.co.jp/recipe/1720001297/", 7 | "foodImageUrl": "http://image.space.rakuten.co.jp/d/strg/ctrl/3/2ffb60f5f4165b3d60025e2be58ae9c4022aac7a.33.2.3.2.jpg", 8 | "mediumImageUrl": "http://image.space.rakuten.co.jp/d/strg/ctrl/3/2ffb60f5f4165b3d60025e2be58ae9c4022aac7a.33.2.3.2.jpg?thum=54", 9 | "smallImageUrl": "http://image.space.rakuten.co.jp/d/strg/ctrl/3/2ffb60f5f4165b3d60025e2be58ae9c4022aac7a.33.2.3.2.jpg?thum=55", 10 | "pickup": 1, 11 | "shop": 0, 12 | "nickname": "k5b4", 13 | "recipeDescription": "下味付けて、フライパンですぐ調理。\n洗いものもなく簡単おいしく。お子様も大好きですよ。", 14 | "recipeMaterial": [ 15 | "鶏むね肉", 16 | "マヨネーズ", 17 | "片栗粉", 18 | "醤油(下味用)", 19 | "砂糖(下味用)", 20 | "すりおろしニンニク(下味用)", 21 | "チリパウダー(カレー粉等)" 22 | ], 23 | "recipeIndication": "約10分", 24 | "recipeCost": "100円以下", 25 | "recipePublishday": "2011/02/27 14:40:56", 26 | "rank": "1" 27 | }, 28 | { 29 | "recipeId": 1290001623, 30 | "recipeTitle": "主人が、いくらでも食べれると絶賛のナス・ピーマン", 31 | "recipeUrl": "http://recipe.rakuten.co.jp/recipe/1290001623/", 32 | "foodImageUrl": "http://image.space.rakuten.co.jp/d/strg/ctrl/3/eb2f27f434436225566c034083f98ddf2aaa0a50.50.2.3.2.jpg", 33 | "mediumImageUrl": "http://image.space.rakuten.co.jp/d/strg/ctrl/3/eb2f27f434436225566c034083f98ddf2aaa0a50.50.2.3.2.jpg?thum=54", 34 | "smallImageUrl": "http://image.space.rakuten.co.jp/d/strg/ctrl/3/eb2f27f434436225566c034083f98ddf2aaa0a50.50.2.3.2.jpg?thum=55", 35 | "pickup": 1, 36 | "shop": 0, 37 | "nickname": "ライム2141", 38 | "recipeDescription": "お弁当のおかずにと思って作ったら、主人が、お弁当箱の半分のスペースは、これでいいよと言うぐらい絶賛してくれたので、我が家の定番おかずになりました(^^♪", 39 | "recipeMaterial": [ 40 | "長ナス", 41 | "ピーマン", 42 | "砂糖", 43 | "醤油", 44 | "ゴマ油orサラダ油", 45 | "だしの素", 46 | "白いりゴマ" 47 | ], 48 | "recipeIndication": "約10分", 49 | "recipeCost": "100円以下", 50 | "recipePublishday": "2011/04/01 14:52:17", 51 | "rank": "2" 52 | }, 53 | { 54 | "recipeId": 1390015585, 55 | "recipeTitle": "ご飯がすすむ♪ 豚肉となすの味噌炒め☆", 56 | "recipeUrl": "http://recipe.rakuten.co.jp/recipe/1390015585/", 57 | "foodImageUrl": "http://image.space.rakuten.co.jp/d/strg/ctrl/3/b9439a7c2fd591879279d91e61d4fb6b09388f27.92.2.3.2.jpg", 58 | "mediumImageUrl": "http://image.space.rakuten.co.jp/d/strg/ctrl/3/b9439a7c2fd591879279d91e61d4fb6b09388f27.92.2.3.2.jpg?thum=54", 59 | "smallImageUrl": "http://image.space.rakuten.co.jp/d/strg/ctrl/3/b9439a7c2fd591879279d91e61d4fb6b09388f27.92.2.3.2.jpg?thum=55", 60 | "pickup": 1, 61 | "shop": 0, 62 | "nickname": "はなまる子♪", 63 | "recipeDescription": "ささっと簡単、豚バラ肉と夏野菜の味噌味の炒めものです。 ご飯のおかずに、おつまみにお箸が進みます。", 64 | "recipeMaterial": [ 65 | "豚バラ肉", 66 | "なす", 67 | "ピーマン", 68 | "オリーブオイル", 69 | "・・調味料", 70 | "A・・", 71 | "味噌、酒", 72 | "砂糖", 73 | "しょうゆ" 74 | ], 75 | "recipeIndication": "約10分", 76 | "recipeCost": "300円前後", 77 | "recipePublishday": "2013/03/26 16:31:30", 78 | "rank": "3" 79 | }, 80 | { 81 | "recipeId": 1400014946, 82 | "recipeTitle": "鶏胸肉で簡単♪手羽風揚げない甘辛照焼チキンお弁当に", 83 | "recipeUrl": "http://recipe.rakuten.co.jp/recipe/1400014946/", 84 | "foodImageUrl": "http://image.space.rakuten.co.jp/d/strg/ctrl/3/3e5906a3607b2f1321cda1158b251c4223204420.40.2.3.2.jpg", 85 | "mediumImageUrl": "http://image.space.rakuten.co.jp/d/strg/ctrl/3/3e5906a3607b2f1321cda1158b251c4223204420.40.2.3.2.jpg?thum=54", 86 | "smallImageUrl": "http://image.space.rakuten.co.jp/d/strg/ctrl/3/3e5906a3607b2f1321cda1158b251c4223204420.40.2.3.2.jpg?thum=55", 87 | "pickup": 1, 88 | "shop": 0, 89 | "nickname": "*ももら*", 90 | "recipeDescription": "鶏胸肉なのにウイング風♬骨が無いので食べ易くお弁当にもピッタリ♡油で揚げないのでヘルシーです♪", 91 | "recipeMaterial": [ 92 | "鶏むね肉", 93 | "片栗粉", 94 | "塩コショウ", 95 | "炒め用サラダ油", 96 | "A醤油", 97 | "Aみりん", 98 | "Aお酒", 99 | "A砂糖" 100 | ], 101 | "recipeIndication": "約10分", 102 | "recipeCost": "300円前後", 103 | "recipePublishday": "2015/09/30 14:28:09", 104 | "rank": "4" 105 | } 106 | ] 107 | } 108 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/travel/area_class_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'rakuten_web_service' 3 | 4 | describe RakutenWebService::Travel::AreaClass do 5 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/Travel/GetAreaClass/20131024' } 6 | let(:affiliate_id) { 'dummy_affiliate_id' } 7 | let(:application_id) { 'dummy_application_id' } 8 | let(:expected_query) do 9 | { 10 | affiliateId: affiliate_id, 11 | applicationId: application_id, 12 | formatVersion: 2 13 | } 14 | end 15 | let(:response) { JSON.parse(fixture('travel/area_class.json')) } 16 | let!(:expected_request) do 17 | stub_request(:get, endpoint) 18 | .with(query: expected_query).to_return(body: response.to_json) 19 | end 20 | 21 | before do 22 | RakutenWebService.configure do |c| 23 | c.affiliate_id = affiliate_id 24 | c.application_id = application_id 25 | end 26 | end 27 | 28 | describe '.search' do 29 | before do 30 | @area_class = RakutenWebService::Travel::AreaClass.search.first 31 | end 32 | 33 | specify 'respond largeClass' do 34 | expect(@area_class['largeClassCode']).to eq('japan') 35 | expect(@area_class['largeClassName']).to eq('日本') 36 | end 37 | specify '#children method responds all middle claasses under a given large class' do 38 | expect(@area_class.children).to_not be_empty 39 | expect(@area_class.children).to be_all do |entity| 40 | entity.is_a?(RWS::Travel::AreaClass::MiddleClass) 41 | end 42 | end 43 | specify '["middle_classes"] responds all middle claasses under a given large class' do 44 | expect(@area_class["middleClasses"]).to_not be_empty 45 | expect(@area_class["middleClasses"]).to be_all do |entity| 46 | entity.is_a?(RWS::Travel::AreaClass::MiddleClass) 47 | end 48 | end 49 | end 50 | 51 | describe '#parent' do 52 | before do 53 | @area_class = RakutenWebService::Travel::AreaClass.search.first 54 | end 55 | 56 | specify 'parent is null' do 57 | expect(@area_class.parent).to be_nil 58 | end 59 | specify 'the self is the parent of children' do 60 | expect(@area_class.children).to be_all do |child| 61 | child.parent == @area_class 62 | end 63 | end 64 | end 65 | 66 | describe '#to_query' do 67 | before do 68 | @area_class = RakutenWebService::Travel::AreaClass.search.first 69 | end 70 | 71 | specify 'As for LargeClass object, returns only its class code.' do 72 | expect(@area_class.to_query).to eq({ 'largeClassCode' => 'japan' }) 73 | end 74 | specify 'As for MiddleClass object, returns only its class code.' do 75 | @area_class.children.each do |child| 76 | expect(child.to_query).to eq({ 'largeClassCode' => 'japan', 'middleClassCode' => child.middle_class_code }) 77 | end 78 | end 79 | end 80 | 81 | describe '.[] method' do 82 | specify 'accepts a area code as argument and respond an class object with the given class code' do 83 | expect(RakutenWebService::Travel::AreaClass::LargeClass['japan']).to be_a(RakutenWebService::Travel::AreaClass::LargeClass) 84 | expect(RakutenWebService::Travel::AreaClass::LargeClass['japan'].large_class_code).to eq('japan') 85 | end 86 | specify 'AreaClass.[] supports' do 87 | expect(RakutenWebService::Travel::AreaClass['japan']).to be_a(RakutenWebService::Travel::AreaClass::LargeClass) 88 | expect(RakutenWebService::Travel::AreaClass['japan'].class_code).to eq('japan') 89 | 90 | expect(RakutenWebService::Travel::AreaClass['hokkaido']).to be_a(RakutenWebService::Travel::AreaClass::MiddleClass) 91 | expect(RakutenWebService::Travel::AreaClass['hokkaido'].class_code).to eq('hokkaido') 92 | end 93 | end 94 | 95 | describe '#search' do 96 | let(:area_class) { RakutenWebService::Travel::AreaClass['sapporo'] } 97 | 98 | specify 'pass area class codes to the simple hotel API' do 99 | expect(RakutenWebService::Travel::Hotel).to receive(:search).with({ 100 | 'largeClassCode' => 'japan', 'middleClassCode' => 'hokkaido', 'smallClassCode' => 'sapporo' 101 | }) 102 | 103 | area_class.search() 104 | end 105 | 106 | context 'Giving params' do 107 | specify 'pass area class codes with the given params to the simple hotel API' do 108 | expect(RakutenWebService::Travel::Hotel).to receive(:search).with({ 109 | 'largeClassCode' => 'japan', 'middleClassCode' => 'hokkaido', 'smallClassCode' => 'sapporo', 110 | 'responseType' => 'large' 111 | }) 112 | 113 | area_class.search('responseType' => 'large') 114 | end 115 | end 116 | end 117 | end 118 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/genre_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe RakutenWebService::BaseGenre do 4 | let(:dummy_endpoint) { 'https://api.example.com/dummy_genre' } 5 | let(:genre_id) { "1" } 6 | let(:expected_response) do 7 | { 8 | 'current' => { 9 | dummyGenreId: 1, 10 | dummyGenreName: "CurrentGenre", 11 | genreLevel: 2 12 | }, 13 | 'children' => [ 14 | { 15 | dummyGenreId: 2, 16 | dummyGenreName: "ChildOne", 17 | genreLevel: 3 18 | }, 19 | { 20 | dummyGenreId: 3, 21 | dummyGenreName: "ChildTwo", 22 | genreLevel: 3 23 | } 24 | ], 25 | 'parents' => [ 26 | { 27 | dummyGenreId: 0, 28 | dummyGenreName: "TopGenre", 29 | genreLevel: 1 30 | } 31 | ], 32 | 'brothers' => [ 33 | { 34 | dummyGenreId: 4, 35 | dummyGenreName: "BrotherOne", 36 | genreLevel: 1 37 | } 38 | ] 39 | } 40 | end 41 | let!(:genre_class) do 42 | 43 | Class.new(RakutenWebService::BaseGenre) do 44 | endpoint "https://api.example.com/dummy_genre" 45 | self.resource_name = 'dummy_genre' 46 | root_id 0 47 | attribute :dummyGenreId, :dummyGenreName, :genreLevel 48 | end 49 | end 50 | 51 | let!(:expected_request) do 52 | stub_request(:get, dummy_endpoint). 53 | with(query: { 54 | applicationId: "DUMMY_APPLICATION_ID", 55 | affiliateId: "DUMMY_AFFILIATE_ID", 56 | formatVersion: 2, 57 | dummyGenreId: genre_id 58 | }). 59 | to_return(body: expected_response.to_json) 60 | end 61 | 62 | before do 63 | RakutenWebService.configure do |c| 64 | c.application_id = "DUMMY_APPLICATION_ID" 65 | c.affiliate_id = "DUMMY_AFFILIATE_ID" 66 | end 67 | end 68 | 69 | after do 70 | RakutenWebService::BaseGenre.instance_variable_set(:@repository, {}) 71 | end 72 | 73 | describe ".genre_id_key" do 74 | it { expect(genre_class.genre_id_key).to be_eql(:dummy_genre_id) } 75 | end 76 | 77 | describe ".root_id" do 78 | it { expect(genre_class.root_id).to be_eql(0) } 79 | end 80 | 81 | describe ".new" do 82 | context "When given string or integer" do 83 | specify "should call search" do 84 | genre_class.new(genre_id) 85 | 86 | expect(expected_request).to have_been_made.once 87 | end 88 | 89 | specify "should call once if the same instance was initialized twice" do 90 | 2.times { genre_class.new(genre_id) } 91 | 92 | expect(expected_request).to have_been_made.once 93 | end 94 | end 95 | 96 | context "When a Hash is given" do 97 | subject { genre_class.new(dummyGenreId: 10, dummyGenreName: "NewGenre") } 98 | 99 | specify "API request should not been called" do 100 | expect(expected_request).to_not have_been_made 101 | end 102 | it "shoud respond apropriate attributes" do 103 | expect(subject.id).to be_eql(10) 104 | expect(subject.name).to be_eql("NewGenre") 105 | end 106 | end 107 | end 108 | 109 | describe ".[]" do 110 | specify "should call search only once as intializing twice" do 111 | 2.times { genre_class[genre_id] } 112 | 113 | expect(expected_request).to have_been_made.once 114 | end 115 | end 116 | 117 | describe "#children" do 118 | subject { genre_class.new(genre_id).children } 119 | 120 | it { is_expected.to_not be_empty } 121 | 122 | context "children's children" do 123 | before do 124 | subject 125 | end 126 | 127 | it "should search again" do 128 | expect(genre_class).to receive(:search). 129 | with(dummy_genre_id: 2). 130 | and_return([ 131 | double(:genre, children: []) 132 | ]) 133 | 134 | expect(subject.first.children).to_not be_nil 135 | expect(subject.first.children).to be_empty 136 | end 137 | end 138 | end 139 | 140 | describe "#parents" do 141 | subject { genre_class.new(genre_id).children } 142 | 143 | it { is_expected.to_not be_empty } 144 | 145 | context "children's parents" do 146 | before do 147 | subject 148 | end 149 | 150 | it "should search again" do 151 | expect(genre_class).to receive(:search). 152 | with(dummy_genre_id: 2). 153 | and_return([ 154 | double(:genre, parents: []) 155 | ]) 156 | 157 | expect(subject.first.parents).to be_empty 158 | expect(subject.first.parents).to_not be_nil 159 | end 160 | end 161 | end 162 | 163 | describe "#brothers" do 164 | subject { genre_class.new(genre_id).children } 165 | 166 | it { is_expected.to_not be_empty } 167 | 168 | context "children's brothers" do 169 | before do 170 | subject 171 | end 172 | 173 | it "should search again" do 174 | expect(genre_class).to receive(:search). 175 | with(dummy_genre_id: 2). 176 | and_return([ 177 | double(:genre, brothers: []) 178 | ]) 179 | 180 | expect(subject.first.brothers).to be_empty 181 | expect(subject.first.brothers).to_not be_nil 182 | end 183 | end 184 | end 185 | end -------------------------------------------------------------------------------- /spec/rakuten_web_service/client_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::Client do 4 | let(:endpoint) { 'https://api.example.com/resources' } 5 | let(:resource_class) do 6 | double('resource_class', endpoint: endpoint) 7 | end 8 | let(:client) { RakutenWebService::Client.new(resource_class) } 9 | let(:application_id) { 'default_application_id' } 10 | let(:affiliate_id) { 'default_affiliate_id' } 11 | let(:expected_query) do 12 | { affiliateId: affiliate_id, applicationId: application_id, formatVersion: '2' } 13 | end 14 | let(:expected_response) do 15 | { body: '{"status":"ok"}' } 16 | end 17 | 18 | before do 19 | @expected_request = stub_request(:get, endpoint). 20 | with(query: expected_query, 21 | headers: { 'User-Agent' => "RakutenWebService SDK for Ruby v#{RWS::VERSION}(ruby-#{RUBY_VERSION} [#{RUBY_PLATFORM}])" }). 22 | to_return(expected_response) 23 | 24 | RakutenWebService.configure do |c| 25 | c.affiliate_id = 'default_affiliate_id' 26 | c.application_id = 'default_application_id' 27 | end 28 | end 29 | 30 | describe '#get' do 31 | context 'call get without any parameters' do 32 | before do 33 | client.get({}) 34 | end 35 | 36 | specify 'call endpoint with default parameters' do 37 | expect(@expected_request).to have_been_made.once 38 | end 39 | end 40 | 41 | context 'call get with parameters' do 42 | let(:affiliate_id) { 'latest_affiliate_id' } 43 | let(:application_id) { 'latest_application_id' } 44 | 45 | before do 46 | client.get(affiliate_id: affiliate_id, 47 | application_id: application_id) 48 | end 49 | 50 | specify 'call endpoint with given parameters' do 51 | expect(@expected_request).to have_been_made.once 52 | end 53 | end 54 | 55 | context "giving 'sort' option" do 56 | let(:expected_query) do 57 | { 58 | applicationId: application_id, 59 | affiliateId: affiliate_id, 60 | formatVersion: '2', 61 | sort: sort_option 62 | } 63 | end 64 | 65 | before do 66 | client.get(sort: sort_option) 67 | end 68 | 69 | context "Specifying asceding order" do 70 | let(:sort_option) { '+itemPrice' } 71 | 72 | specify "encodes '+' in sort option" do 73 | expect(@expected_request).to have_been_made.once 74 | end 75 | end 76 | context "Specifying descending order" do 77 | let(:sort_option) { '-itemPrice' } 78 | 79 | specify "encodes '+' in sort option" do 80 | expect(@expected_request).to have_been_made.once 81 | end 82 | end 83 | end 84 | end 85 | 86 | describe 'about exceptions' do 87 | context 'parameter error' do 88 | let(:expected_response) do 89 | { status: 400, 90 | body: '{"error": "wrong_parameter", 91 | "error_description": "specify valid applicationId"}' 92 | } 93 | end 94 | 95 | specify 'raises WrongParameter exception' do 96 | expect { client.get({}) }.to raise_error(RWS::WrongParameter, 'specify valid applicationId') 97 | end 98 | 99 | end 100 | 101 | context 'Too many requests' do 102 | let(:expected_response) do 103 | { status: 429, 104 | body: '{ "error": "too_many_requests", 105 | "error_description": "number of allowed requests has been exceeded for this API. please try again soon." }' 106 | } 107 | end 108 | 109 | specify 'raises TooManyRequests exception' do 110 | expect { client.get({}) }.to raise_error(RWS::TooManyRequests, 111 | 'number of allowed requests has been exceeded for this API. please try again soon.') 112 | end 113 | end 114 | 115 | context 'Internal error in Rakuten Web Service' do 116 | let(:expected_response) do 117 | { status: 500, 118 | body: '{ "error": "system_error", 119 | "error_description": "api logic error" }' 120 | } 121 | end 122 | 123 | specify 'raises SystemError exception' do 124 | expect { client.get({}) }.to raise_error(RWS::SystemError, 'api logic error') 125 | end 126 | end 127 | 128 | context 'Unavaiable due to maintainance or overloaded' do 129 | let(:expected_response) do 130 | { status: 503, 131 | body: '{ "error": "service_unavailable", 132 | "error_description": "XXX/XXX is under maintenance" }' 133 | } 134 | end 135 | 136 | specify 'raises ServiceUnavailable exception' do 137 | expect { client.get({}) }.to raise_error(RWS::ServiceUnavailable, 'XXX/XXX is under maintenance') 138 | end 139 | end 140 | 141 | context 'The specified genre has no ranking data' do 142 | let(:expected_response) do 143 | { status: 404, 144 | body: '{ "error": "not_found", 145 | "error_description": "This genre data does not exist"}' 146 | } 147 | end 148 | 149 | specify 'raises NotFound exception' do 150 | expect { client.get({}) }.to raise_error(RWS::NotFound, 'This genre data does not exist') 151 | end 152 | end 153 | end 154 | end 155 | -------------------------------------------------------------------------------- /spec/fixtures/gora/course_detail_search.json: -------------------------------------------------------------------------------- 1 | { 2 | "Item": { 3 | "carrier": 0, 4 | "golfCourseId": 120092, 5 | "golfCourseName": "東京湾カントリークラブ", 6 | "golfCourseAbbr": "東京湾CC", 7 | "golfCourseNameKana": "とうきょうわんかんとりーくらぶ", 8 | "golfCourseCaption": "東京湾アクアライン浮島JCTから25分とアクセスにも恵まれ、豊富な自然林を残すフラットな地形に展開する27ホールズ!庭園風の造りの旧ホールと難易度の高い新ホールが見事なまでに調和された戦略性の高いコースです!", 9 | "information": "【コース売店の営業について】\n2017年4月1日よりコース売店は土日祝日のみの営業とさせていただきます。\n予めご了承くださいますようお願い申し上げます。\n※平日は自動販売機のみ利用可能となります。\n※お飲物、軽食はスタート前に1階の売店にてお求めください。\n\n【コースローテション】\n長浦→久保田 久保田→蔵波 蔵波→長浦となります。\nコンペ集計については、合算(他のコースでも合わせて)する事は、出来ます。\n【ニアピン・ドラコン推奨ホール】\n長浦 ドラコンNo8(No4)ニアピンNo5(No7)\n久保田ドラコンNo9(No3)ニアピンNo7(No2)\n蔵波 ドラコンNo9(No2)ニアピンNo3(No1)\n※カッコ付きは第二推奨ホールとなります。\n\n【新規会員募集(補充会員募集)】\n個人正会員、法人正会員:300,000円/1名記名(税別)\n※詳細については、直接ゴルフ場までお電話ください。", 10 | "highway": "館山自動車道", 11 | "ic": "姉崎袖ケ浦", 12 | "icDistance": "5km以内", 13 | "latitude": "35.4346731", 14 | "longitude": "140.0134053", 15 | "postalCode": "299-0243", 16 | "address": "千葉県袖ヶ浦市蔵波1147", 17 | "telephoneNo": "0438-63-3211", 18 | "faxNo": "0438-63-5231", 19 | "openDay": "1979-10-28", 20 | "closeDay": "", 21 | "creditCard": "JCB VISA MASTER ダイナース アメックス", 22 | "shoes": "ソフトスパイクのみ", 23 | "dressCode": "", 24 | "practiceFacility": "あり 130Y 15打席", 25 | "lodgingFacility": "なし", 26 | "otherFacility": "", 27 | "golfCourseImageUrl1": "http://gora.golf.rakuten.co.jp/img/golf/120092/photo1.jpg", 28 | "golfCourseImageUrl2": "http://gora.golf.rakuten.co.jp/img/golf/120092/photo2.jpg", 29 | "golfCourseImageUrl3": "http://gora.golf.rakuten.co.jp/img/golf/120092/photo3.jpg", 30 | "golfCourseImageUrl4": "http://gora.golf.rakuten.co.jp/img/golf/120092/photo4.jpg", 31 | "golfCourseImageUrl5": "http://gora.golf.rakuten.co.jp/img/golf/120092/photo5.jpg", 32 | "weekdayMinPrice": 3590, 33 | "baseWeekdayMinPrice": 3000, 34 | "holidayMinPrice": 6984, 35 | "baseHolidayMinPrice": 6143, 36 | "designer": "吉崎 満雄", 37 | "courseType": "丘陵", 38 | "courseVerticalInterval": "適度なアップダウン", 39 | "dimension": "130万m2", 40 | "green": "ベント", 41 | "greenCount": "1グリーン", 42 | "holeCount": 27, 43 | "parCount": 108, 44 | "courseName": "長浦・久保田・蔵波", 45 | "courseDistance": "10008Y", 46 | "longDrivingContest": "", 47 | "nearPin": "", 48 | "ratingNum": 6402, 49 | "evaluation": 3.6, 50 | "staff": 3.4, 51 | "facility": 3.3, 52 | "meal": 3.6, 53 | "course": 3.6, 54 | "costperformance": 3.7, 55 | "distance": 3.2, 56 | "fairway": 3.1, 57 | "reserveCalUrl": "http://search.gora.golf.rakuten.co.jp/cal/disp/c_id/120092", 58 | "voiceUrl": "http://booking.gora.golf.rakuten.co.jp/voice/detail/c_id/120092", 59 | "layoutUrl": "http://booking.gora.golf.rakuten.co.jp/guide/layout_disp/c_id/120092", 60 | "routeMapUrl": "http://booking.gora.golf.rakuten.co.jp/guide/map/c_id/120092", 61 | "newPlans": [ 62 | { 63 | "month": "5月", 64 | "planName": "<JALロングラン付>早遅食事付3B以上で割増無", 65 | "planDate": "全日", 66 | "service": "乗用カートセルフ昼食付", 67 | "price": "10,500", 68 | "basePrice": 0, 69 | "salesTax": 722, 70 | "courseUseTax": 750, 71 | "otherTax": 0 72 | }, 73 | { 74 | "month": "5月", 75 | "planName": "<JALロングラン付>早遅食事付3B以上で割増無", 76 | "planDate": "全日", 77 | "service": "乗用カートセルフ昼食付", 78 | "price": "11,000", 79 | "basePrice": 0, 80 | "salesTax": 759, 81 | "courseUseTax": 750, 82 | "otherTax": 0 83 | }, 84 | { 85 | "month": "5月", 86 | "planName": "<JALロングラン付>早遅食事付3B以上で割増無", 87 | "planDate": "全日", 88 | "service": "乗用カートセルフ昼食付", 89 | "price": "11,500", 90 | "basePrice": 0, 91 | "salesTax": 796, 92 | "courseUseTax": 750, 93 | "otherTax": 0 94 | } 95 | ], 96 | "ratings": [ 97 | { 98 | "title": "ベスト天候(2017-04-09 14:57:03)", 99 | "nickName": "kazu.f", 100 | "prefecture": "神奈川県", 101 | "age": "60代", 102 | "sex": "女性", 103 | "times": 15, 104 | "evaluation": 5, 105 | "staff": 3, 106 | "facility": 3, 107 | "meal": 4, 108 | "course": 4, 109 | "costperformance": 5, 110 | "distance": 3, 111 | "fairway": 3, 112 | "comment": "今回も天候に恵まれた" 113 | }, 114 | { 115 | "title": "何しろ、近い、安い、面白い。(2017-04-08 21:51:48)", 116 | "nickName": "フミ4004", 117 | "prefecture": "東京都", 118 | "age": "60代", 119 | "sex": "男性", 120 | "times": 34, 121 | "evaluation": 5, 122 | "staff": 4, 123 | "facility": 4, 124 | "meal": 4, 125 | "course": 5, 126 | "costperformance": 5, 127 | "distance": 3, 128 | "fairway": 3, 129 | "comment": "家から、浮島まで少しありますが、アクアラインからあっという間、これからも、利用します。" 130 | }, 131 | { 132 | "title": "親切(2017-04-07 22:29:06)", 133 | "nickName": "2番アイアン", 134 | "prefecture": "東京都", 135 | "age": "30代", 136 | "sex": "男性", 137 | "times": 9, 138 | "evaluation": 5, 139 | "staff": 5, 140 | "facility": 3, 141 | "meal": 4, 142 | "course": 4, 143 | "costperformance": 5, 144 | "distance": 3, 145 | "fairway": 3, 146 | "comment": "職員の方は親切でした。\n前半2時間10分、後半2時間20分でした。待つことは少なかったです。\n安くて良かったです。グリーンはボコボコでしたが。\n要望は帰りのバスの本数が少ないので増やして欲しいことです。" 147 | } 148 | ] 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/recipe/category_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::Recipe::Category do 4 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/Recipe/CategoryList/20170426' } 5 | let(:affiliate_id) { 'dummy_affiliate_id' } 6 | let(:application_id) { 'dummy_application_id' } 7 | let(:expected_query) do 8 | { 9 | affiliateId: affiliate_id, 10 | applicationId: application_id, 11 | formatVersion: '2', 12 | categoryType: category_type 13 | } 14 | end 15 | 16 | before do 17 | RakutenWebService.configure do |c| 18 | c.affiliate_id = affiliate_id 19 | c.application_id = application_id 20 | end 21 | end 22 | 23 | describe '.large_categories' do 24 | let(:category_type) { 'large' } 25 | 26 | before do 27 | response = JSON.parse(fixture('recipe/category.json')) 28 | 29 | @expected_request = stub_request(:get, endpoint). 30 | with(query: expected_query).to_return(body: response.to_json) 31 | end 32 | 33 | after do 34 | RWS::Recipe.instance_variable_set(:@categories, nil) 35 | end 36 | 37 | subject { RakutenWebService::Recipe.large_categories } 38 | 39 | it 'should return' do 40 | expect(subject).to be_a(Array) 41 | end 42 | it 'should call the endpoint once' do 43 | subject.first 44 | 45 | expect(@expected_request).to have_been_made.once 46 | end 47 | it 'should be Category resources' do 48 | expect(subject).to be_all { |c| c.is_a?(RakutenWebService::Recipe::Category) } 49 | end 50 | 51 | context 'When called twice or more' do 52 | specify 'should call the endpoint only once' do 53 | 2.times { RakutenWebService::Recipe.large_categories } 54 | 55 | expect(@expected_request).to have_been_made.once 56 | end 57 | end 58 | end 59 | 60 | describe '.medium_categories' do 61 | it 'should call categories' do 62 | expect(RWS::Recipe).to receive(:categories).with('medium') 63 | 64 | RakutenWebService::Recipe.medium_categories 65 | end 66 | end 67 | 68 | describe '.small_categories' do 69 | it 'should call categories' do 70 | expect(RWS::Recipe).to receive(:categories).with('small') 71 | 72 | RakutenWebService::Recipe.small_categories 73 | 74 | end 75 | end 76 | 77 | describe "#parent_category" do 78 | let(:category) do 79 | RWS::Recipe::Category.new \ 80 | categoryId: 2007, 81 | categoryName: 'もち麦', 82 | categoryType: 'small', 83 | parentCategoryId: '706' 84 | end 85 | let(:category_type) { 'medium' } 86 | 87 | before do 88 | response = JSON.parse(fixture('recipe/category.json')) 89 | 90 | @expected_request = stub_request(:get, endpoint). 91 | with(query: expected_query).to_return(body: response.to_json) 92 | end 93 | 94 | after do 95 | RakutenWebService::Recipe.instance_variable_set(:@categories, nil) 96 | end 97 | 98 | subject { category.parent_category } 99 | 100 | it "should be a Category" do 101 | expect(subject).to be_a(RWS::Recipe::Category) 102 | end 103 | it "should call the endpoint once to get medium categories" do 104 | subject 105 | expect(@expected_request).to have_been_made.once 106 | end 107 | end 108 | 109 | describe '#absolute_category_id' do 110 | let(:category) do 111 | RWS::Recipe::Category.new \ 112 | categoryId: 706, 113 | categoryName: 'もち麦', 114 | categoryType: 'medium', 115 | parentCategoryId: '13' 116 | end 117 | let(:category_type) { 'large' } 118 | 119 | before do 120 | response = JSON.parse(fixture('recipe/category.json')) 121 | 122 | @expected_request = stub_request(:get, endpoint). 123 | with(query: expected_query).to_return(body: response.to_json) 124 | end 125 | 126 | after do 127 | RakutenWebService::Recipe.instance_variable_set(:@categories, nil) 128 | end 129 | 130 | subject { category.absolute_category_id } 131 | 132 | it "should be concatinations with parent category ids" do 133 | expect(subject).to be_eql("13-706") 134 | end 135 | 136 | context 'for small category' do 137 | let(:category) do 138 | RWS::Recipe::Category.new \ 139 | categoryId: 2007, 140 | categoryName: 'もち麦', 141 | categoryType: 'small', 142 | parentCategoryId: '706' 143 | end 144 | 145 | before do 146 | response = JSON.parse(fixture('recipe/category.json')) 147 | 148 | stub_request(:get, endpoint). 149 | with(query: expected_query.merge(categoryType: 'medium')). 150 | to_return(body: response.to_json) 151 | end 152 | 153 | it 'should concatinations with parent category ids' do 154 | expect(subject).to be_eql("13-706-2007") 155 | end 156 | end 157 | end 158 | 159 | describe '#ranking' do 160 | let(:category) do 161 | RWS::Recipe::Category.new \ 162 | categoryId: 706, 163 | categoryName: 'もち麦', 164 | categoryType: 'medium', 165 | parentCategoryId: '13' 166 | end 167 | let(:category_type) { 'large' } 168 | 169 | before do 170 | response = JSON.parse(fixture('recipe/category.json')) 171 | 172 | @expected_request = stub_request(:get, endpoint). 173 | with(query: expected_query).to_return(body: response.to_json) 174 | end 175 | 176 | after do 177 | RakutenWebService::Recipe.instance_variable_set(:@categories, nil) 178 | end 179 | 180 | it 'should call ranking method with category codes' do 181 | expect(RakutenWebService::Recipe).to receive(:ranking).with('13-706') 182 | 183 | category.ranking 184 | end 185 | end 186 | end 187 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/books/genre_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RWS::Books::Genre do 4 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/BooksGenre/Search/20121128' } 5 | let(:affiliate_id) { 'dummy_affiliate_id' } 6 | let(:application_id) { 'dummy_application_id' } 7 | let(:genre_id) { '000' } 8 | let(:expected_query) do 9 | { 10 | affiliateId: affiliate_id, 11 | applicationId: application_id, 12 | formatVersion: '2', 13 | booksGenreId: genre_id 14 | } 15 | end 16 | let(:expected_json) do 17 | JSON.parse(fixture('books/genre_search.json')) 18 | end 19 | 20 | after do 21 | RWS::Books::Genre.instance_variable_set('@repository', {}) 22 | end 23 | 24 | before do 25 | @expected_request = stub_request(:get, endpoint). 26 | with(query: expected_query). 27 | to_return(body: expected_json.to_json) 28 | 29 | RakutenWebService.configure do |c| 30 | c.affiliate_id = affiliate_id 31 | c.application_id = application_id 32 | end 33 | end 34 | 35 | describe '.search' do 36 | before do 37 | @genre = RWS::Books::Genre.search(booksGenreId: genre_id).first 38 | end 39 | 40 | specify 'call the endpoint once' do 41 | expect(@expected_request).to have_been_made.once 42 | end 43 | specify 'has interfaces like hash' do 44 | expect(@genre['booksGenreName']).to eq(expected_json['current']['booksGenreName']) 45 | expect(@genre['genreLevel']).to eq(expected_json['current']['genreLevel']) 46 | end 47 | specify 'has interfaces like hash with snake case key' do 48 | expect(@genre['books_genre_name']).to eq(expected_json['current']['booksGenreName']) 49 | expect(@genre['genre_level']).to eq(expected_json['current']['genreLevel']) 50 | end 51 | specify 'has interfaces to get each attribute' do 52 | expect(@genre.id).to eq(expected_json['current']['booksGenreId']) 53 | expect(@genre.name).to eq(expected_json['current']['booksGenreName']) 54 | end 55 | end 56 | 57 | describe '.new' do 58 | let(:genre_id) { '007' } 59 | let(:expected_json) do 60 | { 61 | current: { 62 | booksGenreId: genre_id, 63 | booksGenreName: 'DummyGenre', 64 | genreLevel: '2' 65 | } 66 | } 67 | end 68 | 69 | before do 70 | @genre = RWS::Books::Genre.new(param) 71 | end 72 | 73 | context 'given a genre id' do 74 | let(:param) { genre_id } 75 | 76 | specify 'only call endpint only at the first time to initialize' do 77 | RWS::Books::Genre.new(param) 78 | 79 | expect(@expected_request).to have_been_made.once 80 | end 81 | end 82 | end 83 | 84 | describe '.root' do 85 | specify 'alias of constructor with the root genre id "000"' do 86 | expect(RWS::Books::Genre).to receive(:new).with('000') 87 | 88 | RWS::Books::Genre.root 89 | end 90 | end 91 | 92 | describe '#children' do 93 | context 'When get search method' do 94 | before do 95 | @genre = RWS::Books::Genre.search(booksGenreId: genre_id).first 96 | end 97 | 98 | specify 'are Books::Genre objects' do 99 | expect(@genre.children).to be_all { |child| child.is_a? RWS::Books::Genre } 100 | expect(@genre.children).to be_all do |child| 101 | expected_json['children'].any? { |c| c['booksGenreId'] == child['booksGenreId'] } 102 | end 103 | end 104 | end 105 | 106 | context 'when the genre object has no children information' do 107 | specify 'call the endpoint to get children' do 108 | genre = RWS::Books::Genre.new(booksGenreId: genre_id) 109 | genre.children 110 | 111 | expect(@expected_request).to have_been_made.once 112 | end 113 | end 114 | end 115 | 116 | describe '#search' do 117 | before do 118 | @genre = RWS::Books::Genre.new(booksGenreId: genre_id) 119 | end 120 | 121 | context 'if the genre_id starts with "001"' do 122 | let(:genre_id) { '001001' } 123 | 124 | specify 'delegate Books::Book.search' do 125 | expect(RWS::Books::Book).to receive(:search).with({booksGenreId: genre_id}) 126 | 127 | @genre.search 128 | end 129 | end 130 | 131 | context 'if the genre_id starts with "002"' do 132 | let(:genre_id) { '002101' } 133 | 134 | specify 'delegate Books::CD.search' do 135 | expect(RWS::Books::CD).to receive(:search).with({booksGenreId: genre_id}) 136 | 137 | @genre.search 138 | end 139 | end 140 | 141 | context 'if the genre_id starts with "003"' do 142 | let(:genre_id) { '003201' } 143 | 144 | specify 'delegate Books::DVD.search' do 145 | expect(RWS::Books::DVD).to receive(:search).with({booksGenreId: genre_id}) 146 | 147 | @genre.search 148 | end 149 | end 150 | 151 | context 'if the genre_id starts with "004"' do 152 | let(:genre_id) { '004301' } 153 | 154 | specify 'delegate Books::Software.search' do 155 | expect(RWS::Books::Software).to receive(:search).with({booksGenreId: genre_id}) 156 | 157 | @genre.search 158 | end 159 | end 160 | 161 | context 'if the genre_id starts with "005"' do 162 | let(:genre_id) { '005401' } 163 | 164 | specify 'delegate Books::ForeignBook.search' do 165 | expect(RWS::Books::ForeignBook).to receive(:search).with({booksGenreId: genre_id}) 166 | 167 | @genre.search 168 | end 169 | end 170 | 171 | context 'if the genre_id starts with "006"' do 172 | let(:genre_id) { '006501' } 173 | 174 | specify 'delegate Books::Game.search' do 175 | expect(RWS::Books::Game).to receive(:search).with({booksGenreId: genre_id}) 176 | 177 | @genre.search 178 | end 179 | end 180 | 181 | context 'if the genre_id starts with "007"' do 182 | let(:genre_id) { '007601' } 183 | 184 | specify 'delegate Books::Magazine.search' do 185 | expect(RWS::Books::Magazine).to receive(:search).with({booksGenreId: genre_id}) 186 | 187 | @genre.search 188 | end 189 | end 190 | end 191 | end 192 | -------------------------------------------------------------------------------- /README.ja.md: -------------------------------------------------------------------------------- 1 | # RakutenWebService 2 | 3 | [![Gem Version](https://badge.fury.io/rb/rakuten_web_service.png)](http://badge.fury.io/rb/rakuten_web_service) 4 | [![Coverage Status](https://coveralls.io/repos/github/rakuten-ws/rws-ruby-sdk/badge.svg?branch=master)](https://coveralls.io/github/rakuten-ws/rws-ruby-sdk?branch=master) 5 | 6 | rakuten\_web\_serviceは、 Rubyから楽天が提供しているAPIに簡単にアクセスできるSDK(Software Development Kit)です。 7 | 8 | English version is [here](http://github.com/rakuten-ws/rws-ruby-sdk/blob/master/README.md). 9 | 10 | ## 前提条件 11 | 12 | * Ruby 3.1 またはそれ以上のバージョンであること 13 | 14 | ## インストール方法 15 | 16 | bundlerを利用したアプリケーションの場合、Gemfileに以下の1行を追加します。 17 | 18 | ```ruby 19 | gem 'rakuten_web_service' 20 | ``` 21 | 22 | そして`bundle`コマンドでインストール。 23 | 24 | ```sh 25 | bundle 26 | ``` 27 | 28 | もしくは、`gem`コマンドにより 29 | 30 | ```sh 31 | gem install rakuten_web_service 32 | ``` 33 | 34 | とすることでインストールできます。 35 | 36 | 現在rakuten\_web\_serviceは下記のAPIをサポートしています。 37 | 38 | ## 使用方法 39 | 40 | ### 事前準備: アプリケーションIDの取得 41 | 42 | 楽天ウェブサービスAPIを利用の際に、アプリケーションIDが必要です。 43 | まだ取得していない場合は、楽天ウェブサービスAPIの[アプリケーション登録](https://webservice.rakuten.co.jp/app/create)を行い、アプリケーションIDを取得してください。 44 | 45 | ### 設定 46 | 47 | `RakutenWebService.configure` メソッドを使い、Application IDとAffiliate ID(オプション)を指定することができます。 48 | 49 | ```ruby 50 | RakutenWebService.configure do |c| 51 | # (必須) アプリケーションID 52 | c.application_id = 'YOUR_APPLICATION_ID' 53 | 54 | # (任意) 楽天アフィリエイトID 55 | c.affiliate_id = 'YOUR_AFFILIATE_ID' # default: nil 56 | 57 | # (任意) リクエストのリトライ回数 58 | # 一定期間の間のリクエスト数が制限を超えた時、APIはリクエスト過多のエラーを返す。 59 | # その後、クライアントは少し間を空けた後に同じリクエストを再度送る。 60 | c.max_retries = 3 # default: 5 61 | 62 | # (任意) デバッグモード 63 | # trueの時、クライアントはすべてのHTTPリクエストとレスポンスを 64 | # 標準エラー出力に流す。 65 | c.debug = true # default: false 66 | end 67 | ``` 68 | 69 | `'YOUR_APPLICATION_ID'` と `'YOUR_AFFILIATE_ID'` は、実際のアプリケーションIDとアフィリエイトIDに置き換えてください。 70 | 71 | #### 環境変数 72 | 73 | `application_id` と `affiliate_id` はそれぞれ、環境変数`RWS_APPLICATION_ID`と`RWS_AFFILIATE_ID`を定義することでも設定できる。 74 | 75 | ### 市場商品の検索 76 | 77 | ```ruby 78 | items = RakutenWebService::Ichiba::Item.search(keyword: 'Ruby') # Enumerable オブジェクトが返ってくる 79 | items.first(10).each do |item| 80 | puts "#{item['itemName']}, #{item.price} yen" # Hashのように値を参照できる 81 | end 82 | ``` 83 | 84 | ### ページング 85 | 86 | `RakutenWebService::Ichiba::Item.search` など`search`メソッドはページングのためのメソッドを持ったオブジェクトを返します。 87 | 88 | ```ruby 89 | items = RakutenWebService::Ichiba::Item.search(keyword: 'Ruby') 90 | items.count #=> 30. デフォルトで1度のリクエストで30件の商品情報が返ってくる 91 | 92 | last_items = items.page(3) # 3ページ目の情報を取る 93 | 94 | # 最後のページまでスキップする 95 | while last_items.next_page? 96 | last_items = last_items.next_page 97 | end 98 | 99 | # 最後のページの商品名を表示 100 | last_items.each do |item| 101 | puts item.name 102 | end 103 | 104 | # 上記の処理をより簡潔に書くと以下のようになる 105 | items.page(3).all do |item| 106 | puts item.name 107 | end 108 | ``` 109 | 110 | ### ジャンル 111 | 112 | Genreクラスは、`children`や`parent`といったジャンル階層を辿るインターフェースを持っています。 113 | 114 | ```ruby 115 | root = RakutenWebService::Ichiba::Genre.root # ジャンルのルート 116 | # children はそのジャンルの直下のサブジャンルを返す 117 | root.children.each do |child| 118 | puts "[#{child.id}] #{child.name}" 119 | end 120 | 121 | # ジャンルの情報を引くため、ジャンルIDを用る 122 | RakutenWebService::Ichiba::Genre[100316].name # => "水・ソフトドリンク" 123 | ``` 124 | 125 | ### 市場商品ランキング 126 | 127 | ```ruby 128 | ranking_by_age = RakutenWebService::Ichiba::Item.ranking(:age => 30, :sex => 1) # 30代女性 のランキングTOP 30 129 | ranking_by_age.each do |ranking| 130 | # 'itemName'以外の属性については右記を参照 https://webservice.rakuten.co.jp/documentation/ichiba-item-ranking#outputParameter 131 | puts ranking.name 132 | end 133 | 134 | ranking_by_genre = RakutenWebService::Ichiba::Genre[200162].ranking # "水・ソフトドリンク" ジャンルのTOP 30 135 | ranking_by_genre.each do |ranking| 136 | puts ranking.name 137 | end 138 | ``` 139 | 140 | ### レシピ 141 | 142 | ```ruby 143 | categories = RakutenWebService::Recipe.small_categories 144 | 145 | # 全種類の小カテゴリーを表示 146 | categories.each do |category| 147 | category.name 148 | end 149 | 150 | recipes = categories.first.ranking 151 | 152 | # カテゴリーに対応するレシピを表示 153 | recipes.each do |recipe| 154 | recipe.title 155 | end 156 | ``` 157 | 158 | ## サポートしているAPI 159 | 160 | ### 楽天市場API 161 | 162 | * [Rakuten Ichiba Item Search API](https://webservice.rakuten.co.jp/documentation/ichiba-item-search/) 163 | * [Rakuten Ichiba Genre Search API](https://webservice.rakuten.co.jp/documentation/ichiba-genre-search/) 164 | * [Rakuten Ichiba Ranking API](https://webservice.rakuten.co.jp/documentation/ichiba-item-ranking/) 165 | * [Rakuten Product API](https://webservice.rakuten.co.jp/documentation/ichiba-product-search/) 166 | * [Rakuten Ichiba Tag Search API](https://webservice.rakuten.co.jp/documentation/ichiba-tag-search/) 167 | 168 | ### 楽天ブックス系API 169 | 170 | * [Rakuten Books Total Search API](https://webservice.rakuten.co.jp/documentation/books-total-search/) 171 | * [Rakuten Books Book Search API](https://webservice.rakuten.co.jp/documentation/books-book-search/) 172 | * [Rakuten Books CD Search API](https://webservice.rakuten.co.jp/documentation/books-cd-search/) 173 | * [Rakuten Books DVD/Blu-ray Search API](https://webservice.rakuten.co.jp/documentation/books-dvd-search/) 174 | * [Rakuten Books ForeignBook Search API](https://webservice.rakuten.co.jp/documentation/books-foreign-search/) 175 | * [Rakuten Books Magazine Search API](https://webservice.rakuten.co.jp/documentation/books-magazine-search/) 176 | * [Rakuten Books Game Search API](https://webservice.rakuten.co.jp/documentation/books-game-search/) 177 | * [Rakuten Books Software Search API](https://webservice.rakuten.co.jp/documentation/books-software-search/) 178 | * [Rakuten Books Genre Search API](https://webservice.rakuten.co.jp/documentation/books-genre-search/) 179 | 180 | ### 楽天Kobo系API 181 | 182 | * [楽天Kobo電子書籍検索API](https://webservice.rakuten.co.jp/documentation/kobo-ebook-search/) 183 | * [楽天Koboジャンル検索API](https://webservice.rakuten.co.jp/documentation/kobo-genre-search/) 184 | 185 | ### 楽天レシピ系API 186 | 187 | * [楽天レシピカテゴリ一覧API](https://webservice.rakuten.co.jp/documentation/recipe-category-list/) 188 | * [楽天レシピカテゴリ別ランキングAPI](https://webservice.rakuten.co.jp/documentation/recipe-category-ranking/) 189 | 190 | ### 楽天GORA系API 191 | 192 | * [楽天GORAゴルフ場検索API](https://webservice.rakuten.co.jp/documentation/gora-golf-course-search/) 193 | * [楽天GORAゴルフ場詳細API](https://webservice.rakuten.co.jp/documentation/gora-golf-course-detail/) 194 | * [楽天GORAプラン検索API](https://webservice.rakuten.co.jp/documentation/gora-plan-search/) 195 | 196 | ### 楽天トラベル系APIs 197 | 198 | * [楽天トラベル施設検索API](https://webservice.rakuten.co.jp/documentation/simple-hotel-search/) 199 | * [楽天トラベル地区コードAPI](https://webservice.rakuten.co.jp/documentation/get-area-class/) 200 | 201 | ## Contributing 202 | 203 | 1. Fork it 204 | 2. Create your feature branch (`git checkout -b my-new-feature`) 205 | 3. Commit your changes (`git commit -am 'Add some feature'`) 206 | 4. Push to the branch (`git push origin my-new-feature`) 207 | 5. Create new Pull Request 208 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/ichiba/genre_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::Ichiba::Genre do 4 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/IchibaGenre/Search/20140222' } 5 | let(:affiliate_id) { 'dummy_affiliate_id' } 6 | let(:application_id) { 'dummy_application_id' } 7 | let(:genre_id) { 0 } 8 | let(:expected_query) do 9 | { 10 | affiliateId: affiliate_id, 11 | applicationId: application_id, 12 | formatVersion: '2', 13 | genreId: genre_id 14 | } 15 | end 16 | let(:fixture_file) { 'ichiba/genre_search.json' } 17 | let(:expected_json) do 18 | JSON.parse(fixture(fixture_file)) 19 | end 20 | 21 | before do 22 | @expected_request = stub_request(:get, endpoint). 23 | with(query: expected_query). 24 | to_return(body: fixture(fixture_file)) 25 | 26 | RakutenWebService.configure do |c| 27 | c.affiliate_id = affiliate_id 28 | c.application_id = application_id 29 | end 30 | end 31 | 32 | describe '.search' do 33 | 34 | before do 35 | @genre = RakutenWebService::Ichiba::Genre.search(genreId: genre_id).first 36 | end 37 | 38 | subject { @genre } 39 | 40 | specify 'should call the endpoint once' do 41 | expect(@expected_request).to have_been_made.once 42 | end 43 | specify 'should be access by key' do 44 | expect(subject['genreName']).to eq(expected_json['current']['genreName']) 45 | expect(subject['genre_name']).to eq(expected_json['current']['genreName']) 46 | end 47 | 48 | describe '#name' do 49 | subject { super().name } 50 | it { is_expected.to eq(expected_json['current']['genreName']) } 51 | end 52 | end 53 | 54 | describe '.new' do 55 | before do 56 | RakutenWebService::Ichiba::Genre.search(genreId: genre_id).first 57 | end 58 | 59 | context 'given genre_id' do 60 | context 'the genre_id has been already fetched' do 61 | before do 62 | @genre = RakutenWebService::Ichiba::Genre.new(genre_id) 63 | end 64 | 65 | subject { @genre } 66 | 67 | specify 'should call the API only once' do 68 | expect(@expected_request).to have_been_made.once 69 | expect(@expected_request).to_not have_been_made.twice 70 | end 71 | specify 'should be access by key' do 72 | expect(subject['genreName']).to eq(expected_json['current']['genreName']) 73 | expect(subject['genre_name']).to eq(expected_json['current']['genreName']) 74 | end 75 | end 76 | 77 | context 'the genre_id has not fetched yet' do 78 | let(:new_genre_id) { 1981 } 79 | before do 80 | @expected_request = stub_request(:get, endpoint). 81 | with(query: { 82 | affiliateId: affiliate_id, 83 | applicationId: application_id, 84 | formatVersion: '2', 85 | genreId: new_genre_id }). 86 | to_return(body: { 87 | current: { 88 | genreId: new_genre_id, 89 | genreName: 'DummyGenre', 90 | genreLevel: 3 91 | } 92 | }.to_json) 93 | 94 | @genre = RakutenWebService::Ichiba::Genre.new(new_genre_id) 95 | end 96 | 97 | specify 'should call the API' do 98 | expect(@expected_request).to have_been_made.once 99 | end 100 | end 101 | end 102 | end 103 | 104 | describe '.root' do 105 | before do 106 | @genre = RakutenWebService::Ichiba::Genre.new(0) 107 | end 108 | 109 | specify 'should be equal genre object with id 0' do 110 | expect(RakutenWebService::Ichiba::Genre.root). 111 | to eq(RakutenWebService::Ichiba::Genre[0]) 112 | end 113 | end 114 | 115 | describe '#children' do 116 | let(:root_genre) { RakutenWebService::Ichiba::Genre.new(genre_id) } 117 | 118 | subject { root_genre.children } 119 | 120 | specify 'children respond genres under the specified genre' do 121 | expect(root_genre.children.size).to eq(expected_json['children'].size) 122 | end 123 | 124 | context 'when children of the genre have not been fetched' do 125 | let(:target_genre) { expected_json['children'].first } 126 | 127 | before do 128 | @additional_request = stub_request(:get, endpoint). 129 | with(query: { 130 | affiliateId: affiliate_id, 131 | applicationId: application_id, 132 | formatVersion: '2', 133 | genreId: target_genre['genreId'] 134 | }).to_return(body: { 135 | current: target_genre, 136 | children: [] 137 | }.to_json) 138 | end 139 | 140 | subject { root_genre.children.first } 141 | 142 | specify 'should call ' do 143 | expect(subject.children.size).to eq(0) 144 | expect(@additional_request).to have_been_made 145 | end 146 | 147 | end 148 | end 149 | 150 | describe '#parents' do 151 | let(:genre_id) { 559887 } 152 | let(:genre) { RakutenWebService::Ichiba::Genre.new(genre_id) } 153 | 154 | before do 155 | stub_request(:get, endpoint).with( 156 | query: { 157 | affiliateId: affiliate_id, 158 | applicationId: application_id, 159 | genreId: genre_id 160 | } 161 | ).to_return(body: fixture('ichiba/parents_genre_search.json')) 162 | end 163 | 164 | specify "should respond an array of parents Genres" do 165 | expect(genre.parents).to be_a(Array) 166 | end 167 | end 168 | 169 | describe "#brothers" do 170 | let(:genre_id) { 201351 } 171 | let(:genre) { RakutenWebService::Ichiba::Genre.new(genre_id) } 172 | let(:fixture_file) { 'ichiba/parents_genre_search.json' } 173 | 174 | specify "should respond an array of brother Genres" do 175 | expect(genre.brothers).to be_a(Array) 176 | end 177 | specify "should have some brothers" do 178 | expect(genre.brothers).to_not be_empty 179 | end 180 | end 181 | 182 | describe '#ranking' do 183 | let(:genre) { RakutenWebService::Ichiba::Genre.new(genre_id) } 184 | 185 | before do 186 | stub_request(:get, endpoint).with(query: expected_query). 187 | to_return(body: fixture('ichiba/genre_search.json')) 188 | end 189 | 190 | specify "should call RankingItem's search with genre_id option" do 191 | expect(RakutenWebService::Ichiba::RankingItem).to receive(:search).with({genre_id: genre_id}) 192 | expect { genre.ranking }.to_not raise_error 193 | end 194 | specify "should call RankingItem's search with genre_id and given options" do 195 | expect(RakutenWebService::Ichiba::RankingItem).to receive(:search).with({genre_id: genre_id, age: 10}) 196 | expect { genre.ranking(age: 10) }.to_not raise_error 197 | end 198 | end 199 | 200 | describe '#products' do 201 | let(:genre) { RakutenWebService::Ichiba::Genre.new(genre_id) } 202 | 203 | before do 204 | stub_request(:get, endpoint).with(query: expected_query). 205 | to_return(body: fixture('ichiba/genre_search.json')) 206 | end 207 | 208 | specify "should call Product search with genre_id option" do 209 | expect(RakutenWebService::Ichiba::Product).to receive(:search).with({genre_id: genre_id}) 210 | expect { genre.products }.to_not raise_error 211 | end 212 | end 213 | end 214 | -------------------------------------------------------------------------------- /spec/rakuten_web_service/ichiba/item_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RakutenWebService::Ichiba::Item do 4 | let(:endpoint) { 'https://app.rakuten.co.jp/services/api/IchibaItem/Search/20220601' } 5 | let(:affiliate_id) { 'dummy_affiliate_id' } 6 | let(:application_id) { 'dummy_application_id' } 7 | let(:expected_query) do 8 | { 9 | affiliateId: affiliate_id, 10 | applicationId: application_id, 11 | formatVersion: '2', 12 | keyword: 'Ruby' 13 | } 14 | end 15 | 16 | before do 17 | RakutenWebService.configure do |c| 18 | c.affiliate_id = affiliate_id 19 | c.application_id = application_id 20 | end 21 | end 22 | 23 | describe '.search' do 24 | before do 25 | response = JSON.parse(fixture('ichiba/item_search_with_keyword_Ruby.json')) 26 | @expected_request = stub_request(:get, endpoint). 27 | with(query: expected_query).to_return(body: response.to_json) 28 | 29 | response['page'] = 2 30 | response['first'] = 31 31 | response['last'] = 60 32 | response['pageCount'] = 2 33 | @second_request = stub_request(:get, endpoint). 34 | with(query: expected_query.merge(page: 2)). 35 | to_return(body: response.to_json) 36 | end 37 | 38 | context 'just call the search method' do 39 | before do 40 | @items = RakutenWebService::Ichiba::Item.search(keyword: 'Ruby') 41 | end 42 | 43 | specify 'endpoint should not be called' do 44 | expect(@expected_request).to_not have_been_made 45 | end 46 | 47 | describe 'a respond object' do 48 | let(:expected_json) do 49 | response = JSON.parse(fixture('ichiba/item_search_with_keyword_Ruby.json')) 50 | response['Items'][0] 51 | end 52 | 53 | subject { @items.first } 54 | 55 | it { is_expected.to be_a RakutenWebService::Ichiba::Item } 56 | specify 'shoud be access by key' do 57 | expect(subject['itemName']).to eq(expected_json['itemName']) 58 | expect(subject['item_name']).to eq(expected_json['itemName']) 59 | end 60 | 61 | describe '#name' do 62 | subject { super().name } 63 | it { is_expected.to eq(expected_json['itemName']) } 64 | end 65 | specify 'should have xxx? method if the object has xxx_flag' do 66 | expect(subject.tax?).to eq(expected_json['taxFlag'] == 1) 67 | end 68 | end 69 | 70 | context 'after that, call each' do 71 | before do 72 | @items.each { |i| i } 73 | end 74 | 75 | specify 'endpoint should be called' do 76 | expect(@expected_request).to have_been_made.once 77 | expect(@second_request).to_not have_been_made 78 | end 79 | end 80 | 81 | context 'chain calling' do 82 | before do 83 | @items2 = @items.search(keyword: 'Go') 84 | end 85 | 86 | specify "2 search resutls should be independent" do 87 | expect(@items.params[:keyword]).to eq('Ruby') 88 | expect(@items2.params[:keyword]).to eq('Go') 89 | end 90 | end 91 | 92 | describe '#all' do 93 | before do 94 | @items.all 95 | end 96 | 97 | specify 'endpoint should not be called' do 98 | expect(@expected_request).to_not have_been_made.once 99 | expect(@second_request).to_not have_been_made.once 100 | end 101 | 102 | context 'call an enumerable method like each' do 103 | before do 104 | @items.all.each { |i| i.to_s } 105 | end 106 | 107 | specify 'endpoint should be called' do 108 | expect(@expected_request).to have_been_made.once 109 | expect(@second_request).to have_been_made.once 110 | end 111 | end 112 | end 113 | 114 | context 'When TooManyRequest error raised' do 115 | let(:client) do 116 | c = double('client') 117 | allow(c).to receive(:get).and_raise(RWS::TooManyRequests) 118 | c 119 | end 120 | 121 | before do 122 | @items.instance_variable_set('@client', client) 123 | end 124 | 125 | specify 'retries automatically after sleeping 1 sec' do 126 | expect(@items).to receive(:sleep).with(1).exactly(5).times.and_return(*([nil] * 5)) 127 | expect { @items.first.name }.to raise_error(RakutenWebService::TooManyRequests) 128 | end 129 | end 130 | end 131 | end 132 | 133 | describe '.search(no items)' do 134 | before do 135 | response = JSON.parse(fixture('ichiba/item_search_with_no_items.json')) 136 | @expected_request = stub_request(:get, endpoint).with(query: expected_query).to_return(body: response.to_json) 137 | end 138 | 139 | subject { RakutenWebService::Ichiba::Item.search(keyword: 'Ruby').first(1) } 140 | specify '' do 141 | expect { subject }.to_not raise_error 142 | end 143 | it { is_expected.to eq [] } 144 | end 145 | 146 | describe '.all' do 147 | before do 148 | response = JSON.parse(fixture('ichiba/item_search_with_keyword_Ruby.json')) 149 | @expected_request = stub_request(:get, endpoint). 150 | with(query: expected_query).to_return(body: response.to_json) 151 | 152 | response['page'] = 2 153 | response['first'] = 31 154 | response['last'] = 60 155 | response['pageCount'] = 2 156 | @second_request = stub_request(:get, endpoint). 157 | with(query: expected_query.merge(page: 2)). 158 | to_return(body: response.to_json) 159 | end 160 | 161 | context 'When givne a block' do 162 | specify '' do 163 | expect { |b| RWS::Ichiba::Item.all({keyword: 'Ruby'}, &b) }.to yield_control.exactly(60).times 164 | 165 | expect { |b| RWS::Ichiba::Item.all({keyword: 'Ruby'}, &b) }.to yield_successive_args(*([RakutenWebService::Ichiba::Item] * 60)) 166 | end 167 | end 168 | end 169 | 170 | describe '.ranking' do 171 | before do 172 | expect(RakutenWebService::Ichiba::RankingItem).to receive(:search).with({}) 173 | end 174 | 175 | specify "call RakutenWebService::Ichiba::RankingItem's search" do 176 | RakutenWebService::Ichiba::Item.ranking 177 | end 178 | end 179 | 180 | describe ".genre_class" do 181 | subject { RakutenWebService::Ichiba::Item } 182 | 183 | it "returns RakutenWebService::Ichiba::Genre" do 184 | expect(subject.genre_class).to eq(RakutenWebService::Ichiba::Genre) 185 | end 186 | end 187 | 188 | describe '#genre' do 189 | let(:response) { JSON.parse(fixture('ichiba/item_search_with_keyword_Ruby.json')) } 190 | 191 | before do 192 | stub_request(:get, endpoint).with(query: expected_query). 193 | to_return(body: response.to_json) 194 | 195 | expected_item = response['Items'][0] 196 | expect(RakutenWebService::Ichiba::Genre).to receive('new').with(expected_item['genreId']) 197 | end 198 | 199 | subject { RakutenWebService::Ichiba::Item.search(keyword: 'Ruby').first.genre } 200 | 201 | specify 'respond Genre object' do 202 | expect { subject }.to_not raise_error 203 | end 204 | end 205 | 206 | describe '#shop' do 207 | let(:response) { JSON.parse(fixture('ichiba/item_search_with_keyword_Ruby.json')) } 208 | let(:expected_item) { response['Items'][0] } 209 | 210 | before do 211 | stub_request(:get, endpoint).with(query: expected_query). 212 | to_return(body: response.to_json) 213 | end 214 | 215 | subject do 216 | RakutenWebService::Ichiba::Item.search(keyword: 'Ruby').first.shop 217 | end 218 | 219 | specify 'responds Shop object' do 220 | expect(subject.name).to eq(expected_item['shopName']) 221 | expect(subject.code).to eq(expected_item['shopCode']) 222 | expect(subject.url).to eq(expected_item['shopUrl']) 223 | expect(subject.affiliate_url).to eq(expected_item['shopAffiliateUrl']) 224 | end 225 | end 226 | 227 | describe '#order' do 228 | specify 'convert sort parameter' do 229 | query = RakutenWebService::Ichiba::Item.search(keyword: 'Ruby').order(affiliate_rate: :desc) 230 | 231 | expect(query.params[:sort]).to eq('-affiliateRate') 232 | end 233 | 234 | specify 'reproduces new SearchResult object' do 235 | first_query = RakutenWebService::Ichiba::Item.search(keyword: 'Ruby') 236 | second_query = first_query.order(affiliate_rate: :desc) 237 | 238 | expect(first_query.params[:sort]).to be_nil 239 | expect(second_query.params[:sort]).to eq('-affiliateRate') 240 | end 241 | end 242 | 243 | describe '#genre_information' do 244 | before do 245 | response = JSON.parse(fixture('ichiba/item_search_with_keyword_Ruby.json')) 246 | @expected_request = stub_request(:get, endpoint). 247 | with(query: expected_query).to_return(body: response.to_json) 248 | end 249 | 250 | subject { RWS::Ichiba::Item.search(keyword: 'Ruby').genre_information } 251 | 252 | it "should be a GenreInformation" do 253 | expect(subject).to be_a(RWS::GenreInformation) 254 | end 255 | end 256 | end 257 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RakutenWebService 2 | 3 | [![CI](https://github.com/rakuten-ws/rws-ruby-sdk/workflows/CI/badge.svg)](https://github.com/rakuten-ws/rws-ruby-sdk/actions?query=workflow%3ACI+branch%3Amaster) 4 | [![Gem Version](https://badge.fury.io/rb/rakuten_web_service.svg)](https://badge.fury.io/rb/rakuten_web_service) 5 | [![Test Coverage](https://codeclimate.com/github/rakuten-ws/rws-ruby-sdk/badges/coverage.svg)](https://codeclimate.com/github/rakuten-ws/rws-ruby-sdk/coverage) 6 | [![Code Climate](https://codeclimate.com/github/rakuten-ws/rws-ruby-sdk/badges/gpa.svg)](https://codeclimate.com/github/rakuten-ws/rws-ruby-sdk) 7 | [![Gitter](https://badges.gitter.im/rakuten-ws/rws-ruby-sdk.svg)](https://gitter.im/rakuten-ws/rws-ruby-sdk?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) 8 | 9 | This gem provides a client for easily accessing [Rakuten Web Service APIs](https://webservice.rakuten.co.jp/). 10 | 11 | 日本語のドキュメントは[こちら](https://github.com/rakuten-ws/rws-ruby-sdk/blob/master/README.ja.md)。 12 | 13 | ## Table of Contents 14 | 15 | * [Prerequisite](#prerequisite) 16 | * [Installation](#installation) 17 | * [Usage](#usage) 18 | * [Prerequisite: Getting Application ID](#prerequisite-getting-application-id) 19 | * [Configuration](#configuration) 20 | * [Search Ichiba Items](#search-ichiba-items) 21 | * [Pagerizing](#pagerizing) 22 | * [Genre](#genre) 23 | * [Ichiba Item Ranking](#ichiba-item-ranking) 24 | * [Supported APIs](#supported-apis) 25 | * [Rakuten Ichiba APIs](#rakuten-ichiba-apis) 26 | * [Rakuten Books APIs](#rakuten-books-apis) 27 | * [Rakuten Kobo APIs](#rakuten-kobo-apis) 28 | * [Rakuten Recipe APIs](#rakuten-recipe-apis) 29 | * [Rakuten GORA APIs](#rakuten-gora-apis) 30 | * [Rakuten Travel APIs](#rakuten-travel-apis) 31 | * [Contributing](#contributing) 32 | 33 | ## Prerequisite 34 | 35 | * Ruby 3.1 or later 36 | 37 | ## Installation 38 | 39 | Add this line to your application's Gemfile: 40 | 41 | ```ruby 42 | gem 'rakuten_web_service' 43 | ``` 44 | 45 | And then execute: 46 | 47 | ```sh 48 | bundle 49 | ``` 50 | 51 | Or install it yourself as: 52 | 53 | ```sh 54 | gem install rakuten_web_service 55 | ``` 56 | 57 | ## Usage 58 | 59 | ### Prerequisite: Getting Application ID 60 | 61 | You need to get Application ID for your application to access to Rakuten Web Service APIs. 62 | If you have not got it, register your application [here](https://webservice.rakuten.co.jp/app/create). 63 | 64 | ### Configuration 65 | 66 | At first, you have to specify your application's key. And you can tell the client your afiiliate id with `RakutenWebService.configure`. 67 | 68 | #### In Your Code 69 | 70 | ```ruby 71 | RakutenWebService.configure do |c| 72 | # (Required) Appliction ID for your application. 73 | c.application_id = 'YOUR_APPLICATION_ID' 74 | 75 | # (Optional) Affiliate ID for your Rakuten account. 76 | c.affiliate_id = 'YOUR_AFFILIATE_ID' # default: nil 77 | 78 | # (Optional) # of retries to send requests when the client receives 79 | # When the number of requests in some period overcomes the limit, the endpoints will return 80 | # too many requests error. Then the client tries to retry to send the same request after a 81 | # while. 82 | c.max_retries = 3 # default: 5 83 | 84 | # (Optional) Enable debug mode. When set true, the client streams out all HTTP requests and 85 | # responses to the standard error. 86 | c.debug = true # default: false 87 | end 88 | ``` 89 | 90 | Please note that you need to replace `'YOUR_APPLICATION_ID'` and `'YOUR_AFFILIATE_ID'` with actual ones you have. 91 | 92 | #### Environment Variables 93 | 94 | You can configure `application_id` and `affiliate_id` by defining environment variables `RWS_APPLICATION_ID` and `RWS_AFFILIATE_ID`. 95 | 96 | ### Search Ichiba Items 97 | 98 | ```ruby 99 | items = RakutenWebService::Ichiba::Item.search(keyword: 'Ruby') # This returns Enumerable object 100 | items.first(10).each do |item| 101 | puts "#{item['itemName']}, #{item.price} yen" # You can refer to values as well as Hash. 102 | end 103 | ``` 104 | 105 | ### Pagerizing 106 | 107 | Responses of resources' `search` such as `RakutenWebService::Ichiba::Item.search` have methods for paginating fetched resources. 108 | 109 | ```ruby 110 | items = RakutenWebService::Ichiba::Item.search(keyword: 'Ruby') 111 | items.count #=> 30. In default, the API returns up to 30 items matched with given keywords. 112 | 113 | last_items = items.page(3) # Skips first 2 pages. 114 | 115 | # Go to the last page 116 | while last_items.next_page? 117 | last_items = last_items.next_page 118 | end 119 | 120 | # Shows the title of the last 30 items 121 | last_items.each do |item| 122 | puts item.name 123 | end 124 | 125 | # Easier way to fetch all resources page 3 and latter 126 | items.page(3).all do |item| 127 | puts item.name 128 | end 129 | ``` 130 | 131 | ### Genre 132 | 133 | Genre class provides an interface to traverse sub genres. 134 | 135 | ```ruby 136 | root = RakutenWebService::Ichiba::Genre.root # root genre 137 | # children returns sub genres 138 | root.children.each do |child| 139 | puts "[#{child.id}] #{child.name}" 140 | end 141 | 142 | # Use genre id to fetch genre object 143 | RakutenWebService::Ichiba::Genre[100316].name # => "水・ソフトドリンク" 144 | ``` 145 | 146 | ### Ichiba Item Ranking 147 | 148 | ```ruby 149 | ranking_by_age = RakutenWebService::Ichiba::Item.ranking(age: 30, sex: 1) # returns the TOP 30 items for Male in 30s 150 | # For attributes other than 'itemName', see: https://webservice.rakuten.co.jp/documentation/ichibaitemsearch/#outputParameter 151 | ranking_by_age.each do |ranking| 152 | puts item.name 153 | end 154 | 155 | ranking_by_genre = RakutenWebService::Ichiba::Genre[200162].ranking # the TOP 30 items in "水・ソフトドリンク" genre 156 | ranking_by_genre.each do |ranking| 157 | puts item.name 158 | end 159 | ``` 160 | 161 | ### Recipe 162 | 163 | ```ruby 164 | categories = RakutenWebService::Recipe.small_categories 165 | 166 | # Search all small recipe categories. 167 | categories.each do |category| 168 | category.name 169 | end 170 | 171 | recipes = categories.first.ranking 172 | 173 | # Search category recipes. 174 | recipes.each do |recipe| 175 | recipe.title 176 | end 177 | ``` 178 | 179 | ## Supported APIs 180 | 181 | Now rakuten\_web\_service is supporting the following APIs: 182 | 183 | ### Rakuten Ichiba APIs 184 | 185 | * [Rakuten Ichiba Item Search API](https://webservice.rakuten.co.jp/documentation/ichiba-item-search/) 186 | * [Rakuten Ichiba Genre Search API](https://webservice.rakuten.co.jp/documentation/ichiba-genre-search/) 187 | * [Rakuten Ichiba Ranking API](https://webservice.rakuten.co.jp/documentation/ichiba-item-ranking/) 188 | * [Rakuten Product API](https://webservice.rakuten.co.jp/documentation/ichiba-product-search/) 189 | * [Rakuten Ichiba Tag Search API](https://webservice.rakuten.co.jp/documentation/ichiba-tag-search/) 190 | 191 | ### Rakuten Books APIs 192 | 193 | * [Rakuten Books Total Search API](https://webservice.rakuten.co.jp/documentation/books-total-search/) 194 | * [Rakuten Books Book Search API](https://webservice.rakuten.co.jp/documentation/books-book-search/) 195 | * [Rakuten Books CD Search API](https://webservice.rakuten.co.jp/documentation/books-cd-search/) 196 | * [Rakuten Books DVD/Blu-ray Search API](https://webservice.rakuten.co.jp/documentation/books-dvd-search/) 197 | * [Rakuten Books ForeignBook Search API](https://webservice.rakuten.co.jp/documentation/books-foreign-search/) 198 | * [Rakuten Books Magazine Search API](https://webservice.rakuten.co.jp/documentation/books-magazine-search/) 199 | * [Rakuten Books Game Search API](https://webservice.rakuten.co.jp/documentation/books-game-search/) 200 | * [Rakuten Books Software Search API](https://webservice.rakuten.co.jp/documentation/books-software-search/) 201 | * [Rakuten Books Genre Search API](https://webservice.rakuten.co.jp/documentation/books-genre-search/) 202 | 203 | ### Rakuten Kobo APIs 204 | 205 | * [Rakuten Kobo Ebook Search API](https://webservice.rakuten.co.jp/documentation/kobo-ebook-search/) 206 | * [Rakuten Kobo Genre Search API](https://webservice.rakuten.co.jp/documentation/kobo-genre-search/) 207 | 208 | ### Rakuten Recipe APIs 209 | 210 | * [Rakuten Recipe Category List API](https://webservice.rakuten.co.jp/documentation/recipe-category-list/) 211 | * [Rakuten Recipe Category Ranking API](https://webservice.rakuten.co.jp/documentation/recipe-category-ranking/) 212 | 213 | ### Rakuten GORA APIs 214 | 215 | * [Rakuten GORA Golf Course Search API](https://webservice.rakuten.co.jp/documentation/gora-golf-course-search/) 216 | * [Rakuten GORA Golf Course Detail Search API](https://webservice.rakuten.co.jp/documentation/gora-golf-course-detail/) 217 | * [Rakuten GORA Plan Search API](https://webservice.rakuten.co.jp/documentation/gora-plan-search/) 218 | 219 | ### Rakuten Travel APIs 220 | 221 | * [Rakuten Travel Simple Hotel API](https://webservice.rakuten.co.jp/documentation/simple-hotel-search/) 222 | * [Rakuten Travel Get Area Class API](https://webservice.rakuten.co.jp/documentation/get-area-class/) 223 | 224 | ## Contributing 225 | 226 | 1. Fork it 227 | 2. Create your feature branch (`git checkout -b my-new-feature`) 228 | 3. Commit your changes (`git commit -am 'Add some feature'`) 229 | 4. Push to the branch (`git push origin my-new-feature`) 230 | 5. Create new Pull Request 231 | --------------------------------------------------------------------------------