Flickraw 和 VCR 给出了意想不到的结果

Flickraw & VCR give unexpected results

我正在使用 VCR 来测试一种从 Flickr

获取照片集列表的方法
require 'rails_helper'

RSpec.describe Photoset, type: :model do
  # ...
  describe '.import' do
    it "gets photosets" do
      VCR.use_cassette('photosets_getList') do
        expect(Photoset.import(user_id: ENV['FLICKR_TEST_USER_ID']).first).to be_a Photoset
      end
    end

    it "sets title" do
      VCR.use_cassette('photosets_getList') do
        expect(Photoset.import(user_id: ENV['FLICKR_TEST_USER_ID']).first.title).to eq 'Showcase'
      end
    end

    it "sets uid" do
      VCR.use_cassette('photosets_getList') do
        expect(Photoset.import(user_id: ENV['FLICKR_TEST_USER_ID']).first.uid).to eq '72157647753138397'
      end
    end
  end
end

该方法本身非常简单,使用 flickraw 从 flickr.photosets.getList 并且它们映射到 Photoset 个对象。

class Photoset
  include Mongoid::Document
  include Mongoid::Timestamps

  field :title, type: String
  field :uid, type: String

  validates_uniqueness_of :uid
  belongs_to :user

  # Get photosets from Flickr
  # @param [Hash] opts
  # @return [Enumerable]
  def self.import(opts = {})
    res = flickr.photosets.getList(opts)
    res.map do |item|
      new(
          uid: item["id"],
          title: item["title"]
      )
    end
  end
end

然而,由于 Flickraw 也向 flickr.reflection.getMethods

发出请求,因此规范会间歇性地失败
flickr.photosets.getList(opts).map { |x| x }

有时 returns 请求方法而不是 Photosets

[("flickr.activity.userComments", "flickr.activity.userPhotos", "flickr.auth.checkToken"...]

是我用错了 VCR 还是 Flickraw 中的错误?

这些是记录的 HTTP 交互:

---
http_interactions:
- request:
    method: post
    uri: https://api.flickr.com/services/rest/
    body:
      encoding: US-ASCII
      string: method=flickr.reflection.getMethods&format=json&nojsoncallback=1
    headers:
      Accept-Encoding:
      - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
      Accept:
      - "*/*"
      User-Agent:
      - FlickRaw/0.9.8
      Content-Type:
      - application/x-www-form-urlencoded
  response:
    status:
      code: 200
      message: OK
    headers:
      Date:
      - Mon, 30 Mar 2015 14:57:38 GMT
      Content-Type:
      - application/json
      Content-Length:
      - '1428'
      P3p:
      - policyref="http://info.yahoo.com/w3c/p3p.xml", CP="CAO DSP COR CUR ADM DEV
        TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY
        ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV"
      Cache-Control:
      - private
      X-Served-By:
      - www235.flickr.bf1.yahoo.com
      Vary:
      - Accept-Encoding
      Age:
      - '0'
      Via:
      - http/1.1 fts124.flickr.bf1.yahoo.com (ApacheTrafficServer/4.0.2 [cMsSf ]),
        http/1.1 r05.ycpi.dea.yahoo.net (ApacheTrafficServer [cMsSf ])
      Server:
      - ATS
      Strict-Transport-Security:
      - max-age=259200
      Connection:
      - keep-alive
    body:
      encoding: UTF-8
      string: '{"methods":{"method":[{"_content":"flickr.activity.userComments"},{"_content":"flickr.activity.userPhotos"},{"_content":"flickr.auth.checkToken"},{"_content":"flickr.auth.getFrob"},{"_content":"flickr.auth.getFullToken"},{"_content":"flickr.auth.getToken"},{"_content":"flickr.auth.oauth.checkToken"},{"_content":"flickr.auth.oauth.getAccessToken"},{"_content":"flickr.blogs.getList"},{"_content":"flickr.blogs.getServices"},{"_content":"flickr.blogs.postPhoto"},{"_content":"flickr.cameras.getBrandModels"},{"_content":"flickr.cameras.getBrands"},{"_content":"flickr.collections.getInfo"},{"_content":"flickr.collections.getTree"},{"_content":"flickr.commons.getInstitutions"},{"_content":"flickr.contacts.getList"},{"_content":"flickr.contacts.getListRecentlyUploaded"},{"_content":"flickr.contacts.getPublicList"},{"_content":"flickr.contacts.getTaggingSuggestions"},{"_content":"flickr.favorites.add"},{"_content":"flickr.favorites.getContext"},{"_content":"flickr.favorites.getList"},{"_content":"flickr.favorites.getPublicList"},{"_content":"flickr.favorites.remove"},{"_content":"flickr.galleries.addPhoto"},{"_content":"flickr.galleries.create"},{"_content":"flickr.galleries.editMeta"},{"_content":"flickr.galleries.editPhoto"},{"_content":"flickr.galleries.editPhotos"},{"_content":"flickr.galleries.getInfo"},{"_content":"flickr.galleries.getList"},{"_content":"flickr.galleries.getListForPhoto"},{"_content":"flickr.galleries.getPhotos"},{"_content":"flickr.groups.browse"},{"_content":"flickr.groups.discuss.replies.add"},{"_content":"flickr.groups.discuss.replies.delete"},{"_content":"flickr.groups.discuss.replies.edit"},{"_content":"flickr.groups.discuss.replies.getInfo"},{"_content":"flickr.groups.discuss.replies.getList"},{"_content":"flickr.groups.discuss.topics.add"},{"_content":"flickr.groups.discuss.topics.getInfo"},{"_content":"flickr.groups.discuss.topics.getList"},{"_content":"flickr.groups.getInfo"},{"_content":"flickr.groups.join"},{"_content":"flickr.groups.joinRequest"},{"_content":"flickr.groups.leave"},{"_content":"flickr.groups.members.getList"},{"_content":"flickr.groups.pools.add"},{"_content":"flickr.groups.pools.getContext"},{"_content":"flickr.groups.pools.getGroups"},{"_content":"flickr.groups.pools.getPhotos"},{"_content":"flickr.groups.pools.remove"},{"_content":"flickr.groups.search"},{"_content":"flickr.interestingness.getList"},{"_content":"flickr.machinetags.getNamespaces"},{"_content":"flickr.machinetags.getPairs"},{"_content":"flickr.machinetags.getPredicates"},{"_content":"flickr.machinetags.getRecentValues"},{"_content":"flickr.machinetags.getValues"},{"_content":"flickr.panda.getList"},{"_content":"flickr.panda.getPhotos"},{"_content":"flickr.people.findByEmail"},{"_content":"flickr.people.findByUsername"},{"_content":"flickr.people.getGroups"},{"_content":"flickr.people.getInfo"},{"_content":"flickr.people.getLimits"},{"_content":"flickr.people.getPhotos"},{"_content":"flickr.people.getPhotosOf"},{"_content":"flickr.people.getPublicGroups"},{"_content":"flickr.people.getPublicPhotos"},{"_content":"flickr.people.getUploadStatus"},{"_content":"flickr.photos.addTags"},{"_content":"flickr.photos.comments.addComment"},{"_content":"flickr.photos.comments.deleteComment"},{"_content":"flickr.photos.comments.editComment"},{"_content":"flickr.photos.comments.getList"},{"_content":"flickr.photos.comments.getRecentForContacts"},{"_content":"flickr.photos.delete"},{"_content":"flickr.photos.geo.batchCorrectLocation"},{"_content":"flickr.photos.geo.correctLocation"},{"_content":"flickr.photos.geo.getLocation"},{"_content":"flickr.photos.geo.getPerms"},{"_content":"flickr.photos.geo.photosForLocation"},{"_content":"flickr.photos.geo.removeLocation"},{"_content":"flickr.photos.geo.setContext"},{"_content":"flickr.photos.geo.setLocation"},{"_content":"flickr.photos.geo.setPerms"},{"_content":"flickr.photos.getAllContexts"},{"_content":"flickr.photos.getContactsPhotos"},{"_content":"flickr.photos.getContactsPublicPhotos"},{"_content":"flickr.photos.getContext"},{"_content":"flickr.photos.getCounts"},{"_content":"flickr.photos.getExif"},{"_content":"flickr.photos.getFavorites"},{"_content":"flickr.photos.getInfo"},{"_content":"flickr.photos.getNotInSet"},{"_content":"flickr.photos.getPerms"},{"_content":"flickr.photos.getRecent"},{"_content":"flickr.photos.getSizes"},{"_content":"flickr.photos.getUntagged"},{"_content":"flickr.photos.getWithGeoData"},{"_content":"flickr.photos.getWithoutGeoData"},{"_content":"flickr.photos.licenses.getInfo"},{"_content":"flickr.photos.licenses.setLicense"},{"_content":"flickr.photos.notes.add"},{"_content":"flickr.photos.notes.delete"},{"_content":"flickr.photos.notes.edit"},{"_content":"flickr.photos.people.add"},{"_content":"flickr.photos.people.delete"},{"_content":"flickr.photos.people.deleteCoords"},{"_content":"flickr.photos.people.editCoords"},{"_content":"flickr.photos.people.getList"},{"_content":"flickr.photos.recentlyUpdated"},{"_content":"flickr.photos.removeTag"},{"_content":"flickr.photos.search"},{"_content":"flickr.photos.setContentType"},{"_content":"flickr.photos.setDates"},{"_content":"flickr.photos.setMeta"},{"_content":"flickr.photos.setPerms"},{"_content":"flickr.photos.setSafetyLevel"},{"_content":"flickr.photos.setTags"},{"_content":"flickr.photos.suggestions.approveSuggestion"},{"_content":"flickr.photos.suggestions.getList"},{"_content":"flickr.photos.suggestions.rejectSuggestion"},{"_content":"flickr.photos.suggestions.removeSuggestion"},{"_content":"flickr.photos.suggestions.suggestLocation"},{"_content":"flickr.photos.transform.rotate"},{"_content":"flickr.photos.upload.checkTickets"},{"_content":"flickr.photosets.addPhoto"},{"_content":"flickr.photosets.comments.addComment"},{"_content":"flickr.photosets.comments.deleteComment"},{"_content":"flickr.photosets.comments.editComment"},{"_content":"flickr.photosets.comments.getList"},{"_content":"flickr.photosets.create"},{"_content":"flickr.photosets.delete"},{"_content":"flickr.photosets.editMeta"},{"_content":"flickr.photosets.editPhotos"},{"_content":"flickr.photosets.getContext"},{"_content":"flickr.photosets.getInfo"},{"_content":"flickr.photosets.getList"},{"_content":"flickr.photosets.getPhotos"},{"_content":"flickr.photosets.orderSets"},{"_content":"flickr.photosets.removePhoto"},{"_content":"flickr.photosets.removePhotos"},{"_content":"flickr.photosets.reorderPhotos"},{"_content":"flickr.photosets.setPrimaryPhoto"},{"_content":"flickr.places.find"},{"_content":"flickr.places.findByLatLon"},{"_content":"flickr.places.getChildrenWithPhotosPublic"},{"_content":"flickr.places.getInfo"},{"_content":"flickr.places.getInfoByUrl"},{"_content":"flickr.places.getPlaceTypes"},{"_content":"flickr.places.getShapeHistory"},{"_content":"flickr.places.getTopPlacesList"},{"_content":"flickr.places.placesForBoundingBox"},{"_content":"flickr.places.placesForContacts"},{"_content":"flickr.places.placesForTags"},{"_content":"flickr.places.placesForUser"},{"_content":"flickr.places.resolvePlaceId"},{"_content":"flickr.places.resolvePlaceURL"},{"_content":"flickr.places.tagsForPlace"},{"_content":"flickr.prefs.getContentType"},{"_content":"flickr.prefs.getGeoPerms"},{"_content":"flickr.prefs.getHidden"},{"_content":"flickr.prefs.getPrivacy"},{"_content":"flickr.prefs.getSafetyLevel"},{"_content":"flickr.push.getSubscriptions"},{"_content":"flickr.push.getTopics"},{"_content":"flickr.push.subscribe"},{"_content":"flickr.push.unsubscribe"},{"_content":"flickr.reflection.getMethodInfo"},{"_content":"flickr.reflection.getMethods"},{"_content":"flickr.stats.getCollectionDomains"},{"_content":"flickr.stats.getCollectionReferrers"},{"_content":"flickr.stats.getCollectionStats"},{"_content":"flickr.stats.getCSVFiles"},{"_content":"flickr.stats.getPhotoDomains"},{"_content":"flickr.stats.getPhotoReferrers"},{"_content":"flickr.stats.getPhotosetDomains"},{"_content":"flickr.stats.getPhotosetReferrers"},{"_content":"flickr.stats.getPhotosetStats"},{"_content":"flickr.stats.getPhotoStats"},{"_content":"flickr.stats.getPhotostreamDomains"},{"_content":"flickr.stats.getPhotostreamReferrers"},{"_content":"flickr.stats.getPhotostreamStats"},{"_content":"flickr.stats.getPopularPhotos"},{"_content":"flickr.stats.getTotalViews"},{"_content":"flickr.tags.getClusterPhotos"},{"_content":"flickr.tags.getClusters"},{"_content":"flickr.tags.getHotList"},{"_content":"flickr.tags.getListPhoto"},{"_content":"flickr.tags.getListUser"},{"_content":"flickr.tags.getListUserPopular"},{"_content":"flickr.tags.getListUserRaw"},{"_content":"flickr.tags.getMostFrequentlyUsed"},{"_content":"flickr.tags.getRelated"},{"_content":"flickr.test.echo"},{"_content":"flickr.test.login"},{"_content":"flickr.test.null"},{"_content":"flickr.urls.getGroup"},{"_content":"flickr.urls.getUserPhotos"},{"_content":"flickr.urls.getUserProfile"},{"_content":"flickr.urls.lookupGallery"},{"_content":"flickr.urls.lookupGroup"},{"_content":"flickr.urls.lookupUser"}]},"stat":"ok"}'
    http_version: 
  recorded_at: Mon, 30 Mar 2015 14:57:37 GMT
- request:
    method: post
    uri: https://api.flickr.com/services/rest/
    body:
      encoding: US-ASCII
      string: user_id=62829091%40N05&method=flickr.photosets.getList&format=json&nojsoncallback=1
    headers:
      Accept-Encoding:
      - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
      Accept:
      - "*/*"
      User-Agent:
      - FlickRaw/0.9.8
      Content-Type:
      - application/x-www-form-urlencoded
  response:
    status:
      code: 200
      message: OK
    headers:
      Date:
      - Mon, 30 Mar 2015 14:57:38 GMT
      Content-Type:
      - application/json
      Content-Length:
      - '878'
      P3p:
      - policyref="http://info.yahoo.com/w3c/p3p.xml", CP="CAO DSP COR CUR ADM DEV
        TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY
        ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE LOC GOV"
      Cache-Control:
      - private
      X-Served-By:
      - www35.flickr.bf1.yahoo.com
      Vary:
      - Accept-Encoding
      Age:
      - '0'
      Via:
      - http/1.1 fts109.flickr.bf1.yahoo.com (ApacheTrafficServer/4.0.2 [cMsSf ]),
        http/1.1 r14.ycpi.dea.yahoo.net (ApacheTrafficServer [cMsSf ])
      Server:
      - ATS
      Strict-Transport-Security:
      - max-age=259200
      Connection:
      - keep-alive
    body:
      encoding: UTF-8
      string: '{"photosets":{"page":1,"pages":1,"perpage":10,"total":10,"photoset":[{"id":"72157647753138397","primary":"13362508473","secret":"73945d3830","server":"7300","farm":8,"photos":"10","videos":0,"title":{"_content":"Showcase"},"description":{"_content":""},"needs_interstitial":0,"visibility_can_see_set":1,"count_views":"0","count_comments":"0","can_comment":0,"date_create":"1411992696","date_update":"1420398843"},{"id":"72157642806447225","primary":"13362324935","secret":"792aed9e58","server":"7072","farm":8,"photos":"11","videos":0,"title":{"_content":"Offpistcamp,
        Mars 2014"},"description":{"_content":""},"needs_interstitial":0,"visibility_can_see_set":1,"count_views":"18","count_comments":"0","can_comment":0,"date_create":"1395604793","date_update":"1420398843"},{"id":"72157635592985815","primary":"9789159454","secret":"68e3687ebf","server":"7304","farm":8,"photos":"49","videos":0,"title":{"_content":"Sara
        & Adam\u00b4s wedding "},"description":{"_content":""},"needs_interstitial":0,"visibility_can_see_set":1,"count_views":"36","count_comments":"0","can_comment":0,"date_create":"1379447652","date_update":"1379448808"},{"id":"72157633225753747","primary":"8643524170","secret":"d31134cfac","server":"8387","farm":9,"photos":"11","videos":0,"title":{"_content":"\u00c5re
        Park"},"description":{"_content":""},"needs_interstitial":0,"visibility_can_see_set":1,"count_views":"0","count_comments":"0","can_comment":0,"date_create":"1365784604","date_update":"1365784720"},{"id":"72157632691639823","primary":"8446828475","secret":"c27f229c79","server":"8079","farm":9,"photos":"5","videos":0,"title":{"_content":"\u00c5resj\u00f6n"},"description":{"_content":""},"needs_interstitial":0,"visibility_can_see_set":1,"count_views":"0","count_comments":"0","can_comment":0,"date_create":"1360071999","date_update":"1360072052"},{"id":"72157632228500139","primary":"8266649775","secret":"bea16448f6","server":"8337","farm":9,"photos":"36","videos":0,"title":{"_content":"Renskiljning"},"description":{"_content":""},"needs_interstitial":0,"visibility_can_see_set":1,"count_views":"8","count_comments":"0","can_comment":0,"date_create":"1355334260","date_update":"1365536767"},{"id":"72157632141702707","primary":"8234153651","secret":"82b1bababb","server":"8487","farm":9,"photos":"11","videos":0,"title":{"_content":"Ozone
        Demodag 2012"},"description":{"_content":""},"needs_interstitial":0,"visibility_can_see_set":1,"count_views":"30","count_comments":"0","can_comment":0,"date_create":"1354372862","date_update":"1354373227"},{"id":"72157629510040228","primary":"6956421098","secret":"fe55925613","server":"7197","farm":8,"photos":"14","videos":0,"title":{"_content":"Slutskjutet
        p\u00e5 Skutskjutet"},"description":{"_content":""},"needs_interstitial":0,"visibility_can_see_set":1,"count_views":"4","count_comments":"0","can_comment":0,"date_create":"1335109758","date_update":"1335109926"},{"id":"72157626719466442","primary":"5719219791","secret":"5966d75b4d","server":"2128","farm":3,"photos":"4","videos":0,"title":{"_content":"Trondheim"},"description":{"_content":""},"needs_interstitial":0,"visibility_can_see_set":1,"count_views":"1","count_comments":"0","can_comment":0,"date_create":"1305400311","date_update":"1354372544"},{"id":"72157626594657331","primary":"5719668226","secret":"d5c9c6e1bd","server":"2593","farm":3,"photos":"15","videos":0,"title":{"_content":"Rope
        Swing Session"},"description":{"_content":"Oslo, norway"},"needs_interstitial":0,"visibility_can_see_set":1,"count_views":"8","count_comments":"0","can_comment":0,"date_create":"1305398192","date_update":"1305398195"}]},"stat":"ok"}'
    http_version: 
  recorded_at: Mon, 30 Mar 2015 14:57:38 GMT
recorded_with: VCR 2.9.3

flickr.reflection.getMethods 被调用来构建所有的对象和方法。 Flickraw 在初始化时不知道有哪些方法可用。如果你想要一个确定性的行为,你可以使用 flickraw-cached 它嵌入了所有方法的列表。

https://github.com/hanklords/flickraw/issues/86#issuecomment-96834525