jjzjj

dy对接接口获取数据

扶我起来继续学习 2024-01-22 原文

1.需求方:

1.已知账户,获取该账户下每天的发视频数据,同时获取一个视频连续30天的点赞数,分享数,评论数。
2.需求方确定在标题中附带来源和作者相关信息,从标题中提取该部分信息,作为原创和作者绩效考核。

2.实现方:

1.在douyin开放平台中注册,并绑定该抖音账户,获取APPID和APP_SECRET。

3.实现过程:

3.1 授权步骤一 通过用户授权获取授权码code

该接口只适用于抖音获取授权临时票据(code)。

注意:

抖音的 OAuth API 以https://open.douyin.com/开头。 该 URL 不是用来请求的,
需要展示给用户用于扫码,在抖音 APP 支持端内唤醒的版本内打开的话会弹出客户端原生授权页面。 获取的 code
可以用来调用 oauth/access_token/ 换取用户 acccess_token。
若需要授权多个 scope 需要把多个 scope 使用英文 “,” 拼接,例如 scope1,scope2,scope3 。
使用本接口前提:

首先你需要去官网申请,使你的应用可以使用特定的 Scope,具体需要哪些 Scope,请查看各接口定义。 其次你需要在本 URL 的
scope 字段中填上用户需要授权给你的 Scope。 用户授权通过后,你才可以调用相应的接口。

       /**
     * 抖音授权
     *
     * @param request
     * @param response
     */
    @GetMapping("/videoConfig/douYin/getCode")
    @ResponseBody
    public R getDouYinCode(HttpServletRequest request, HttpServletResponse response) {
        VideoConfig dyInfo = videoConfigMapper.selectByType("dy");

        if (dyInfo == null) {
            logger.error("=============无抖音平台数据==============");
            throw new RuntimeException("无抖音平台数据");
        }
        // https://open.douyin.com/platform/oauth/connect/?client_key=xxxxxxx&response_type=code&scope=video.list&redirect_uri=https://baidu.com/login
        //需要用户开放的权限
        String scope = "trial.whitelist,video.list,renew_refresh_token";
        //回调地址 https://baidu.com/login 为临时回调地址的,正式上线要用线上的域名
        //VIDEO_AUTH_CALLBACK_URL 为网站的回调域名
        String redirect_uri = VIDEO_AUTH_CALLBACK_URL + "/douYin/authCallback";
        String requestUrl = "https://open.douyin.com/platform/oauth/connect/?client_key=" + dyInfo.getAppId()
                + "&response_type=code" + "&scope=" + scope + "&redirect_uri=" + redirect_uri;
        return R.ok().data("url", requestUrl);

    }
    }

3.2 授权步骤二 获取access_token

该接口用于获取用户授权第三方接口调用的凭证 access_token;该接口适用于抖音/头条授权。

注意:

抖音的 OAuth API 以https://open.douyin.com/开头。 头条的 OAuth API
以https://open.snssdk.com/开头。 西瓜的 OAuth API
以https://open-api.ixigua.com/开头。 access_token
为用户授权第三方接口调用的凭证,存储在客户端,可能会被窃取,泄漏后可能会发生用户隐私数据泄漏的风险,建议存储在服务端。 获取到
access_token 后授权临时票据 (code) 不要再授权刷新,否则会导致上一次获取的 code 过期。

上面/videoConfig/douYin/getCode请求将回调下面请求地址,并在request中返回code,然后请求获取token,并记录access_token,refresh_token ,expires_in,refresh_expires_in,open_id

 @GetMapping("/douYin/authCallback")
    @ResponseBody
    public String dyAuthCallback(HttpServletRequest request) throws ParseException {
        // 请求参数
        VideoConfig dyInfo = videoConfigMapper.selectByType("dy");
        //请求参数
        String code = request.getParameter("code");
//        String code = "xxxxxxxxxxxxxxxxxx";
        Map<String, String> map = new HashMap<>();
        map.put("client_key", dyInfo.getAppId());
        map.put("client_secret", dyInfo.getAppSecret());
        map.put("code", code);
        map.put("grant_type", "authorization_code");

        //请求地址
        String url = dyInfo.getGetTokenUrl();
        HttpRequest httpRequest = HttpRequest.get(url, map, Boolean.TRUE);
        String result = httpRequest.body();
        JSONObject jasonObject = JSONObject.parseObject(result);
        JSONObject data = (JSONObject) jasonObject.get("data");
        System.out.println("jasonObject = " + jasonObject);
        int errorCode = Integer.parseInt(data.get("error_code").toString());
        if (errorCode == 0) {
            //查询数据库里面的内容
            String open_id = data.get("open_id").toString();
            String access_token = data.get("access_token").toString();
            String refresh_token = data.get("refresh_token").toString();
            //记录token,将他的过期时间也记录下来,查询时过期,就去更新
            long expires_in = Long.parseLong(data.get("expires_in").toString());
            long refresh_expires_in = Long.parseLong(data.get("refresh_expires_in").toString());
            long currentTimeMillis = System.currentTimeMillis();
            long tokenExpiresInLong = currentTimeMillis + expires_in * 1000;
            long refreshExpiresInLong = currentTimeMillis + refresh_expires_in * 1000;
            String tokenExpiresIn = TimeUtil.transferLongToDate("yyyy-MM-dd HH:mm:ss", tokenExpiresInLong);
            String refreshExpiresIn = TimeUtil.transferLongToDate("yyyy-MM-dd HH:mm:ss", refreshExpiresInLong);
            dyInfo.setOpenId(open_id);
            dyInfo.setToken(access_token);
            dyInfo.setRefreshToken(refresh_token);
            dyInfo.setTokenExpiresIn(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(tokenExpiresIn));
            dyInfo.setRefreshExpiresIn(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(refreshExpiresIn));
            videoConfigMapper.updateByPrimaryKeySelective(dyInfo);
            logger.info("accessToken记录成功");
            return "授权成功,请关闭当前窗口!";
        }
        return "授权失败,请联系管理人员";
    }

3.3 授权步骤三 刷新access_token

 /**
     * 刷新 access_token 的有效期
     */
    private void refresh_token() throws ParseException {
        logger.info("=================抖音刷新 access_token 的有效期=================");
        VideoConfig dyInfo = videoConfigMapper.selectByType("dy");
        String client_key = dyInfo.getAppId();
        Map<String, String> map = new HashMap<>();
        map.put("client_key", client_key);
        map.put("grant_type", "refresh_token");
        map.put("refresh_token", dyInfo.getRefreshToken());
        //请求地址
        String url = dyInfo.getGetRefreshTokenUrl();
        HttpRequest httpRequest = HttpRequest.post(url, map, Boolean.TRUE).header("Content-Type", "multipart/form-data");
        String result = httpRequest.body();
        JSONObject jasonObject = JSONObject.parseObject(result);
        JSONObject data = (JSONObject) jasonObject.get("data");
        logger.info("=================抖音刷新刷新access_token 的有效期:{}=================", jasonObject);

        String errorCode = data.get("error_code").toString();
        if (errorCode.equals("10008") || errorCode.equals("2190008")) {
            //这表示access_token过期 需要重新获取refresh_token 后会获取一个新的 access_token 以及新的超时时间。
            //说明要刷新重新获取refresh_token
            logger.info("=================抖音刷新刷新重新获取refresh_token=================");
            renew_refresh_token();
        } else {
            //未过期,提取并记录刷新后的access_token
            if (errorCode.equals("0")) {
                logger.info("=================抖音 未过期,提取并记录刷新后的access_token=================");
                String open_id = data.get("open_id").toString();
                String access_token = data.get("access_token").toString();
                //记录token,将他的过期时间也记录下来,查询时过期,就去更新
                long expires_in = Long.parseLong(data.get("expires_in").toString());
                long refresh_expires_in = Long.parseLong(data.get("refresh_expires_in").toString());
                long currentTimeMillis = System.currentTimeMillis();

                long tokenExpiresInLong = currentTimeMillis + expires_in * 1000;
                long refreshExpiresInLong = currentTimeMillis + refresh_expires_in * 1000;

                String tokenExpiresIn = TimeUtil.transferLongToDate("yyyy-MM-dd HH:mm:ss", tokenExpiresInLong);
                String refreshExpiresIn = TimeUtil.transferLongToDate("yyyy-MM-dd HH:mm:ss", refreshExpiresInLong);
                dyInfo.setOpenId(open_id);
                dyInfo.setToken(access_token);
                dyInfo.setRefreshToken(dyInfo.getRefreshToken());
                dyInfo.setTokenExpiresIn(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(tokenExpiresIn));
                dyInfo.setRefreshExpiresIn(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(refreshExpiresIn));
                videoConfigMapper.updateByPrimaryKeySelective(dyInfo);
                logger.info("accessToken记录成功");

            }
        }
        // 提取并记录刷新后的access_token 获取成功后再执行一次获取视频列表数据
        // 如果access_token刷新后则进行renew_refresh_token操作
    }

3.4 授权步骤四 刷新refresh_token

 /**
     * 该接口用于刷新refresh_token的有效期
     */
    private void renew_refresh_token() throws ParseException {
        VideoConfig dyInfo = videoConfigMapper.selectByType("dy");
        String client_key = dyInfo.getAppId();
        Map<String, String> map = new HashMap<>();
        map.put("client_key", client_key);
        map.put("refresh_token", dyInfo.getRefreshToken());
        //请求地址
        String url = dyInfo.getGetRenewRefreshTokenUrl();
        HttpRequest httpRequest = HttpRequest.post(url, map, Boolean.TRUE).header("Content-Type", "multipart/form-data");
        String result = httpRequest.body();
        logger.info("=================刷新refresh_token的有效期,数据获取result:{}=================", result);

        JSONObject jasonObject = JSONObject.parseObject(result);
        JSONObject data = (JSONObject) jasonObject.get("data");
        logger.info("=================刷新refresh_token的有效期,数据获取jasonObject:{}=================", jasonObject);

        String errorCode = data.get("error_code").toString();
        //未过期,提取并记录刷新后的access_token
        if (errorCode.equals("0")) {
            //过期时间 30天
            long expires_in = Long.parseLong(data.get("expires_in").toString());
            String refresh_token = data.get("refresh_token").toString();

            long currentTimeMillis = System.currentTimeMillis();
            System.out.println("currentTimeMillis = " + currentTimeMillis);
            long refreshExpiresInLong = currentTimeMillis + expires_in * 1000;
            String refreshExpiresIn = TimeUtil.transferLongToDate("yyyy-MM-dd HH:mm:ss", refreshExpiresInLong);

            dyInfo.setRefreshToken(refresh_token);
            dyInfo.setRefreshExpiresIn(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(refreshExpiresIn));
            videoConfigMapper.updateByPrimaryKeySelective(dyInfo);
            logger.info("刷新refresh_token记录成功");
        }else{
			//TODO 给管理员发一个短信需要他认证。
		}
    }

3.5 获取视频列表

1.执行一个定时器任务,进行数据抓取,并记录数据库中。
2.抖音每次只支持十条数据返回,每次不能全部抓取,分批次读取数据,找到30天内的数据,30天内的数据是需要更新总点击数和总分享数等。
3.列表是有规律的,除指定的几个其余是按照当前时间逆序排序的。
作者可能有多个人,因此每个数据插入前要进行人员匹配哦

 /**
     * 获取视频列表并插入数据库    每天凌晨2点30分抓取
     */
    @Scheduled(cron = "${dy.task.cron}")
    public void getVideoList() throws ParseException {
        /**
         * open_id = 
         * access_token = 
         */
        VideoConfig dyInfo = videoConfigMapper.selectByType("dy");

        if (dyInfo == null) {
            throw new RuntimeException("无数据抖音平台数据");
        }
		//检查是否需要刷新access_token
        if (dyInfo.getTokenExpiresIn().getTime() < System.currentTimeMillis()) {
            //为空则说明需要去刷新 获取老版本然后更新
            refresh_token();
            //刷新后再次获取最新的access_token相关信息
            dyInfo = videoConfigMapper.selectByType("dy");
        }

        //获取列表
        String has_more = null;
        String cursor = "0";
        int count = 0;
        JSONArray jsonArray = new JSONArray();
        do {

            JSONObject list10 = getList10(dyInfo, cursor);
            if (list10 == null) return;
            has_more = list10.get("has_more").toString();
            cursor = list10.get("cursor").toString();
            JSONArray list = (JSONArray) list10.get("list");
            for (int i = 0; i < list.size(); i++) {
                JSONObject json = (JSONObject) list.get(i);
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                String date = json.get("create_time").toString();
                String createTime = sdf.format(new Date(Long.parseLong(String.valueOf(Long.parseLong(date) * 1000))));    //时间戳转化成时间

                boolean flag = TimeUtil.isPastNDays(createTime, 30);
                if (!flag) {
                    //不在该时间范围就移除数据.
                    list.remove(i);
                    System.out.println("移除时间为:" + TimeUtil.TimeStampToTime(date));
                    System.out.println("移除的标题为:" + json.get("title").toString());
                    //动态调整结束请求页数,当从第二页开始,如果有连续移除时,说明后面的页数据已经不是要取的了.
                    if (count >= 2) {
                        has_more = "false";
                    }
                }
            }
            jsonArray.addAll(list);

            //标记页数
            count++;

        } while (has_more.equals("true"));

        logger.info("本次翻页{}次,共采集到有效数据{}条", count, jsonArray.size());
        dataDeal(jsonArray);
        logger.info("=================抖音数据采集结束=================");
    }
 //数据处理过程
    private void dataDeal(JSONArray jsonArray) throws ParseException {
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject jsonObject = jsonArray.getJSONObject(i);
            //视频创建时间戳
            String date = jsonObject.get("create_time").toString();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            String createTime = sdf.format(new Date(Long.parseLong(String.valueOf(Long.parseLong(date) * 1000))));    //时间戳转化成时间
            //网页链接
            String share_url = jsonObject.get("share_url").toString();
            //标题
            String title = jsonObject.get("title").toString();

            String layout = "";
            String authors = "";
            String editor = "";
            String editor2 = "";
            String editor3 = "";
            String editor4 = "";

            if (title.indexOf("来源:") > 0||title.indexOf("来源:") > 0) { //判断是否有这个
                String str1 = "";
                if(title.indexOf("来源:")>0){
                    str1 = title.substring(0, title.indexOf("来源:"));
                }else{
                    str1 = title.substring(0, title.indexOf("来源:"));
                }
                //获取作者相关信息,处理格式问题
                String contactInfo = title.substring(str1.length()).replace(" ", "").replace("\n", "");
                logger.info("=================抖音数据获取contactInfo:{}=================", contactInfo);
                Pattern from = Pattern.compile("来源:.+作者|编辑");
                Matcher fromM = from.matcher(contactInfo);

                while (fromM.find()) {
                    //获取作者信息然后去匹配字段
                    layout = contactInfo.substring(fromM.start(), fromM.end()).replace("来源:", "").replace("编辑", "");
                    if (layout.contains("xxxxx")) { //xxxx表示来来源后的一段文字
                        //自己的媒体有作者信息
                        // 以 编辑:开头,以 编辑: 结束的字符串
                        Pattern p = Pattern.compile("作者:.+编辑:");
                        Matcher m = p.matcher(contactInfo);
                        while (m.find()) {
                            //获取作者信息然后去匹配字段
                            authors = contactInfo.substring(m.start(), m.end()).replace("作者:", "").replace("编辑", "");
                            logger.info("作者:" + authors);
                        }
                    }
                }
                //获取编辑
                Pattern ed = Pattern.compile("编辑:.+责编");
                Matcher em = ed.matcher(contactInfo);
                while (em.find()) {
                    //获取作者信息然后去匹配字段
                    editor = contactInfo.substring(em.start(), em.end()).replace("编辑:", "").replace("责编", "");
                    logger.info("编辑:" + editor);
                }
                //获取责编
                Pattern ed2 = Pattern.compile("责编:.+编审");
                Matcher em2 = ed2.matcher(contactInfo);

                while (em2.find()) {
                    //获取作者信息然后去匹配字段
                    editor2 = contactInfo.substring(em2.start(), em2.end()).replace("责编:", "").replace("编审", "");
                    logger.info("责编:" + editor2);
                }
                //获取编审
                Pattern ed3 = Pattern.compile("编审:.+监制");
                Matcher em3 = ed3.matcher(contactInfo);

                while (em3.find()) {
                    //获取作者信息然后去匹配字段
                    editor3 = contactInfo.substring(em3.start(), em3.end()).replace("编审:", "").replace("监制", "");
                    logger.info("编审:" + editor3);
                }
                //获取监制
                Pattern ed4 = Pattern.compile("监制:.+");
                Matcher em4 = ed4.matcher(contactInfo);

                while (em4.find()) {
                    //获取监制信息然后去匹配字段
                    editor4 = contactInfo.substring(em4.start(), em4.end()).replace("监制:", "");
                    logger.info("监制:" + editor4);
                }
            }

            //统计数据
            JSONObject statistics = (JSONObject) jsonObject.get("statistics");            //分享数
            //播放数
            int play_count = (int) statistics.get("play_count");

            //点赞数
            int digg_count = (int) statistics.get("digg_count");

            //分享数
            int share_count = (int) statistics.get("share_count");

            //首先根据时间和标题看是否已经插入了
            title = title.replace("\n", "");
            Audit audi = auditMapper.selectByTitleAndTime(title, createTime);
            //如果已经入库则更新总点击和总分享量
            if (audi == null) {
                Audit audit = new Audit();
                audit.setCreateTime(createTime);
                audit.setMediaName("抖音");
                audit.setType("video");
                audit.setTitle(title);
                audit.setLayout(layout);
                audit.setLink(share_url);
                audit.setReadCount(play_count);
                audit.setClickTotal(digg_count);
                audit.setShareCount(share_count);
                audit.setReadTotal(play_count);
                audit.setClickTotal(digg_count);
                audit.setShareTotal(share_count);
                insertAudit(authors, editor, editor2, editor3, editor4, audit);

            } else {
                logger.info("================= 更新数据 =================");
                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
                String currentDate = df.format(new Date());
                int dayNum = TimeUtil.daysBetween(createTime, currentDate);

                //未审核并且时间再30天内
                if (audi.getIsAudit() < 2 && dayNum <= 30) {
                    audi.setReadTotal(audi.getReadTotal() + play_count);
                    audi.setClickTotal(audi.getClickTotal() + digg_count);
                    audi.setShareTotal(audi.getShareTotal() + share_count);
                    //更新
                    auditMapper.updateByPrimaryKeySelective(audi);
                    logger.info("更新一条数据成功,{},{}", audi.getTitle(), audi.getCreateTime());
                }


            }
        }
    }

    //获取数据
    private JSONObject getList10(VideoConfig dyInfo, String cursor) {
        //数据读取
        String open_id = dyInfo.getOpenId();
        String access_token = dyInfo.getToken();

        Map<String, String> map = new HashMap<>();
        map.put("open_id", open_id);
        map.put("cursor", cursor);
        map.put("count", "10");
        logger.info("=================抖音数据采集开始=================");
        //请求地址
        String url = dyInfo.getGetVideoListUrl();
        HttpRequest httpRequest = HttpRequest.get(url, map, Boolean.TRUE).header("access-token", access_token);
        String result = httpRequest.body();
        JSONObject jasonObject = JSONObject.parseObject(result);

        JSONObject data = (JSONObject) jasonObject.get("data");
        if (!data.get("error_code").toString().equals("0")) {
            logger.error("抖音数据获取失败:{}", jasonObject);
            return null;
        }
        JSONArray jsonArray = (JSONArray) data.get("list");
        logger.info("=================抖音数据获取,接口响应数据:{}=================", jasonObject);
        logger.info("数据条数为:{}", jsonArray.size());
        return data;
    }

	//开启一个事务,要么都成功插入数据库,要么都失败会滚
    @Transactional(rollbackFor = Exception.class)
    public void insertAudit(String authors, String editor, String editor2, String editor3, String editor4, Audit audit) {
        try {
            auditMapper.insertSelective(audit);
            logger.info("新加一条数据成功,{},{}", audit.getTitle(), audit.getCreateTime());
            //执行插入作者信息
            authorDoUtil.insertAuthorInfoPlus(audit.getAuditId(), authors, AuthorType.AUTHOR);
            //执行编辑作者信息
            authorDoUtil.insertAuthorInfoPlus(audit.getAuditId(), editor, AuthorType.EDITOR);
            //执行插入责编信息
            authorDoUtil.insertAuthorInfoPlus(audit.getAuditId(), editor2, AuthorType.EX_EDITOR);
            //执行插入编审信息
            authorDoUtil.insertAuthorInfoPlus(audit.getAuditId(), editor3, AuthorType.EDITORIAL);
            //执行插入监制信息
            authorDoUtil.insertAuthorInfoPlus(audit.getAuditId(), editor4, AuthorType.SUPERVISOR);

        } catch (Exception e) {
            logger.error("执行抖音数据采集失败,回滚成功!标题为:{},{},{}", audit.getTitle(), audit.getCreateTime(), e.getMessage());
            //手动回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(),使得事务生效,出现异常回滚。
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

        }
    }

有关dy对接接口获取数据的更多相关文章

  1. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  2. ruby - 简单获取法拉第超时 - 2

    有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url

  3. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  4. ruby - 从 Ruby 中的主机名获取 IP 地址 - 2

    我有一个存储主机名的Ruby数组server_names。如果我打印出来,它看起来像这样:["hostname.abc.com","hostname2.abc.com","hostname3.abc.com"]相当标准。我想要做的是获取这些服务器的IP(可能将它们存储在另一个变量中)。看起来IPSocket类可以做到这一点,但我不确定如何使用IPSocket类遍历它。如果它只是尝试像这样打印出IP:server_names.eachdo|name|IPSocket::getaddress(name)pnameend它提示我没有提供服务器名称。这是语法问题还是我没有正确使用类?输出:ge

  5. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

  6. ruby-on-rails - 获取 inf-ruby 以使用 ruby​​ 版本管理器 (rvm) - 2

    我安装了ruby​​版本管理器,并将RVM安装的ruby​​实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby​​。有没有办法让emacs像shell一样尊重ruby​​的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el

  7. Ruby 从大范围中获取第 n 个项目 - 2

    假设我有这个范围:("aaaaa".."zzzzz")如何在不事先/每次生成整个项目的情况下从范围中获取第N个项目? 最佳答案 一种快速简便的方法:("aaaaa".."zzzzz").first(42).last#==>"aaabp"如果出于某种原因你不得不一遍又一遍地这样做,或者如果你需要避免为前N个元素构建中间数组,你可以这样写:moduleEnumerabledefskip(n)returnto_enum:skip,nunlessblock_given?each_with_indexdo|item,index|yieldit

  8. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  9. ruby - 没有类方法获取 Ruby 类名 - 2

    如何在Ruby中获取BasicObject实例的类名?例如,假设我有这个:classMyObjectSystem我怎样才能使这段代码成功?编辑:我发现Object的实例方法class被定义为returnrb_class_real(CLASS_OF(obj));。有什么方法可以从Ruby中使用它? 最佳答案 我花了一些时间研究irb并想出了这个:classBasicObjectdefclassklass=class这将为任何从BasicObject继承的对象提供一个#class您可以调用的方法。编辑评论中要求的进一步解释:假设你有对象

  10. ruby-on-rails - 如何在 Gem 中获取 Rails 应用程序的根目录 - 2

    是否可以在应用程序中包含的gem代码中知道应用程序的Rails文件系统根目录?这是gem来源的示例:moduleMyGemdefself.included(base)putsRails.root#returnnilendendActionController::Base.send:include,MyGem谢谢,抱歉我的英语不好 最佳答案 我发现解决类似问题的解决方案是使用railtie初始化程序包含我的模块。所以,在你的/lib/mygem/railtie.rbmoduleMyGemclassRailtie使用此代码,您的模块将在

随机推荐