草庐IT

mongodb - 访问 map 时的 Goroutine block

coder 2024-07-12 原文

以下代码片段取自旨在从 MongoDB 读取文档并将其写入 Postgres 数据库的程序。 该程序是使用生产者/消费者模式实现的:生产者 Goroutine 从 Mongo 读取并将获取的文档发送到 channel 。
consumer Goroutine 从 channel 中读取数据,构造一个 INSERT INTO sql 语句并将数据插入到 Postgres 数据库中。

不幸的是,消费者 似乎不确定地阻止。我相信当访问从 producerconsumerchan 传递的 map 数据结构时,就会发生这种情况,但不能确定。

生产者的简化代码:

func producer(ops chan BatchOp, ...) {
    // Iterate over all tables that we want to fetch documents from
    for _, table := range schema.Schema[0].Tables {

        // Run some aggregation to fetch the documents and create an iterator
        pipe := mongoDb.C(table.Collection).Pipe(table.Pipeline)
        iter := pipe.Batch(200).AllowDiskUse().Iter()

        const RESULTS_BUFFER_SIZE = 200


        result := bson.M{}
        results := make([]bson.M, 0, RESULTS_BUFFER_SIZE)

        // The flush function sends a batch of data into the channel
        flush := func() {
            fmt.Printf("Flushing %d items\n", len(results))
            ops <- BatchOp{
                Insert,
                table.Collection,
                results,
            }
            results = make([]bson.M, 0, RESULTS_BUFFER_SIZE)
        }

        // Iterate over the results from MongoDB and append each document 
        // to the results slice
        for iter.Next(&result) {
            results = append(results, bson.M{
                "_id": result["_id"],
                "name": result["Name"],
                "i": result["i"],
            })

            // ... flush the results slice as soon as its big enough
            if len(results) == RESULTS_BUFFER_SIZE {
                flush()
            }
        }

        // Flush the last contents of the results slice
        if len(results) > 0 {
            flush()
        }

        if err := iter.Close(); err != nil {
            panic(err)
        }
    }
}


type BatchOp struct {
    data       []bson.M // bson.M is defined as: type M map[string]interface{} 
}
ops := make(chan BatchOp)

不幸的是,当消费者访问数据时,程序不确定地阻塞:

func writer(schema *db.SchemaSchema, ops chan BatchOp, psql *sql.DB) {
    // ...

    for {
        op := <-ops

        fmt.Printf("Writing %d rows\n", len(op.data))
        _, err := psql.Exec(db.GenerateInsert(table, op.data))
        // ...
        fmt.Printf("Wrote %d rows\n", len(op.data))
    }
}

// Generate the INSERT INTO statement for a slice of documents from MongoDB
func GenerateInsert(table Table, data []bson.M) (sql string) {
    sql = fmt.Sprintf(`INSERT INTO "%s" (`, table.Table)
    for i, column := range table.Columns {
        sql += `"` + column.SqlName + `"`
        if i < len(table.Columns)-1 {
            sql += ", "
        }
    }

    sql += ") VALUES "
    for i, row := range data {
        sql += "("
        for j, column := range table.Columns {

            // !!!!!! THIS !!!!!!! is the row that blocks and would just not get called sometimes
            switch v := row[column.Name].(type) {
            case nil:
                sql += "NULL"
            case int:
                sql += fmt.Sprintf(`%v`, v)
            default:
                sql += fmt.Sprintf(`'%v'`, v)
            }

            if j < len(table.Columns)-1 {
                sql += ", "
            }
        }
        sql += ")"

        if i < len(data)-1 {
            sql += ", "
        }
    }

    return sql
}
  • 导致此语句 row[column.Name] 阻塞的问题可能是什么?我相信这与通过 chan 发送深度层次化的 map 这一事实有关。
  • 我怎么调试这个? GDB?
  • 像这样实现生产者-消费者是最佳实践吗?

编辑 7 月 24 日
* 对完整代码的引用,因为评论者要求它:https://github.com/erikmuttersbach/mongo2psql

  • 使我得出代码块结论的示例输出: 当我运行 go run insert.go 时,1111 个文档被插入到 foos 集合中。
    当我随后运行 go run main.go 时,我得到以下输出: 创建表 foos 创建表格栏 从集合 foos 中播种表 foos 法拉盛 200 项目 写入 200 行 法拉盛 200 项目 写了 200 行 写入 200 行 法拉盛 200 项目 写了 200 行 写入 200 行 法拉盛 200 项目 写了 200 行 写入 200 行 法拉盛 200 项目 写了 200 行 写入 200 行 法拉盛 111 项目 写了 200 行 写入 111 行 从收集栏中播种表栏 播种完成
    Writing 111 rows 行之后,我希望另一行 Wrote 111 rows 不会出现(它有时也会发生)。
    当您将 GenerateInsert 的函数体替换为简单的 return "SELECT 1" 时,您可以得到 Wrote 111 rows 来显示。由于在这种情况下省略了对 channel 消息的访问,我假设它与访问 channel 消息的 data 有关。

** 编辑 7 月 24 日 (2)** 当我用 SIGQUIT 终止程序时,我得到以下输出:

^\SIGQUIT: quit
PC=0x10566a3 m=0 sigcode=0

goroutine 0 [idle]:
runtime.mach_semaphore_timedwait(0xf03, 0x186a0, 0x7fff5fbff3ec, 0x0, 0x186a05fbff428, 0x1455b60, 0x7fff5fbff420, 0x1050d63, 0x186a0, 0xffffffff, ...)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/sys_darwin_amd64.s:425 +0x13
runtime.semasleep1(0x186a0, 0xffffffff)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/os_darwin.go:402 +0xe1
runtime.semasleep.func1()
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/os_darwin.go:432 +0x33
runtime.systemstack(0x7fff5fbff448)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/asm_amd64.s:343 +0xab
runtime.semasleep(0x186a0, 0xffffffff)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/os_darwin.go:433 +0x4d
runtime.notetsleep_internal(0x1455808, 0x186a0, 0x1455b60, 0x14d44e02e4b89c75, 0x1455b00)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/lock_sema.go:198 +0x79
runtime.notetsleep(0x1455808, 0x186a0, 0x0)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/lock_sema.go:246 +0x75
runtime.stopTheWorldWithSema()
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/proc.go:1029 +0x274
runtime.systemstack(0x1455b00)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/asm_amd64.s:327 +0x79
runtime.mstart()
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/proc.go:1132

goroutine 26 [running]:
runtime.systemstack_switch()
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/asm_amd64.s:281 fp=0xc42004d608 sp=0xc42004d600
runtime.gcStart(0x0, 0x12ec200)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/mgc.go:1010 +0x208 fp=0xc42004d640 sp=0xc42004d608
runtime.mallocgc(0x2000, 0x0, 0xc420114300, 0xc420248000)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/malloc.go:785 +0x491 fp=0xc42004d6e0 sp=0xc42004d640
runtime.rawstring(0x1f60, 0x0, 0x0, 0x0, 0x0, 0x0)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/string.go:237 +0x85 fp=0xc42004d710 sp=0xc42004d6e0
runtime.rawstringtmp(0x0, 0x1f60, 0x13, 0x1f5e, 0xc420248000, 0x1f5e, 0x1f5e)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/string.go:107 +0x78 fp=0xc42004d750 sp=0xc42004d710
runtime.concatstrings(0x0, 0xc42004d830, 0x2, 0x2, 0xc420248000, 0x1f5e)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/string.go:46 +0xf9 fp=0xc42004d7e8 sp=0xc42004d750
runtime.concatstring2(0x0, 0xc420248000, 0x1f5e, 0x130caaa, 0x2, 0xc420248000, 0x1f5e)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/string.go:55 +0x47 fp=0xc42004d828 sp=0xc42004d7e8
_/Users/erikmuttersbach/Projects/mongo2psql-go/db.GenerateInsert(0xc420103870, 0x4, 0xc420103790, 0x4, 0xc4201098c0, 0x1, 0x4, 0xc420124100, 0x3, 0x4, ...)
    /Users/erikmuttersbach/Projects/mongo2psql-go/db/sql.go:31 +0x46f fp=0xc42004da10 sp=0xc42004d828
main.writer(0xc420108200, 0xc4200728a0, 0xc4200c0280)
    /Users/erikmuttersbach/Projects/mongo2psql-go/main.go:111 +0x3b4 fp=0xc42004dfc8 sp=0xc42004da10
runtime.goexit()
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/asm_amd64.s:2197 +0x1 fp=0xc42004dfd0 sp=0xc42004dfc8
created by main.main
    /Users/erikmuttersbach/Projects/mongo2psql-go/main.go:181 +0x135

goroutine 1 [running]:
    goroutine running on other thread; stack unavailable

goroutine 17 [syscall, 10 minutes, locked to thread]:
runtime.goexit()
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/asm_amd64.s:2197 +0x1

goroutine 20 [chan receive, 10 minutes]:
database/sql.(*DB).connectionOpener(0xc4200c0280)
    /usr/local/Cellar/go/1.8.3/libexec/src/database/sql/sql.go:837 +0x4a
created by database/sql.Open
    /usr/local/Cellar/go/1.8.3/libexec/src/database/sql/sql.go:582 +0x212

goroutine 22 [sleep, 10 minutes]:
time.Sleep(0x1dcd6500)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/time.go:59 +0xf9
gopkg.in/mgo%2ev2.(*mongoCluster).syncServersLoop(0xc42006c500)
    /Users/erikmuttersbach/go/src/gopkg.in/mgo.v2/cluster.go:368 +0x471
created by gopkg.in/mgo%2ev2.newCluster
    /Users/erikmuttersbach/go/src/gopkg.in/mgo.v2/cluster.go:78 +0x188

goroutine 50 [sleep, 10 minutes]:
time.Sleep(0x37e11d600)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/time.go:59 +0xf9
gopkg.in/mgo%2ev2.(*mongoServer).pinger(0xc42010e000, 0x1)
    /Users/erikmuttersbach/go/src/gopkg.in/mgo.v2/server.go:301 +0x293
created by gopkg.in/mgo%2ev2.newServer
    /Users/erikmuttersbach/go/src/gopkg.in/mgo.v2/server.go:89 +0x166

goroutine 25 [select, 10 minutes]:
main.tailer(0xc4200728a0, 0xc4200fe0a0)
    /Users/erikmuttersbach/Projects/mongo2psql-go/main.go:67 +0x4d0
created by main.main
    /Users/erikmuttersbach/Projects/mongo2psql-go/main.go:180 +0xff

goroutine 38 [IO wait]:
net.runtime_pollWait(0x1664df0, 0x72, 0xa)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/netpoll.go:164 +0x59
net.(*pollDesc).wait(0xc42006f5d8, 0x72, 0x1438e40, 0x14363d0)
    /usr/local/Cellar/go/1.8.3/libexec/src/net/fd_poll_runtime.go:75 +0x38
net.(*pollDesc).waitRead(0xc42006f5d8, 0xc42007d6b0, 0x24)
    /usr/local/Cellar/go/1.8.3/libexec/src/net/fd_poll_runtime.go:80 +0x34
net.(*netFD).Read(0xc42006f570, 0xc42007d6b0, 0x24, 0x24, 0x0, 0x1438e40, 0x14363d0)
    /usr/local/Cellar/go/1.8.3/libexec/src/net/fd_unix.go:250 +0x1b7
net.(*conn).Read(0xc4200e0010, 0xc42007d6b0, 0x24, 0x24, 0x0, 0x0, 0x0)
    /usr/local/Cellar/go/1.8.3/libexec/src/net/net.go:181 +0x70
gopkg.in/mgo%2ev2.fill(0x143cce0, 0xc4200e0010, 0xc42007d6b0, 0x24, 0x24, 0x0, 0x53)
    /Users/erikmuttersbach/go/src/gopkg.in/mgo.v2/socket.go:535 +0x53
gopkg.in/mgo%2ev2.(*mongoSocket).readLoop(0xc420164000)
    /Users/erikmuttersbach/go/src/gopkg.in/mgo.v2/socket.go:551 +0x107
created by gopkg.in/mgo%2ev2.newSocket
    /Users/erikmuttersbach/go/src/gopkg.in/mgo.v2/socket.go:194 +0x259

goroutine 3 [runnable]:
net.runtime_pollWait(0x1664eb0, 0x72, 0x9)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/netpoll.go:164 +0x59
net.(*pollDesc).wait(0xc4201060d8, 0x72, 0x1438e40, 0x14363d0)
    /usr/local/Cellar/go/1.8.3/libexec/src/net/fd_poll_runtime.go:75 +0x38
net.(*pollDesc).waitRead(0xc4201060d8, 0xc4201000c0, 0x24)
    /usr/local/Cellar/go/1.8.3/libexec/src/net/fd_poll_runtime.go:80 +0x34
net.(*netFD).Read(0xc420106070, 0xc4201000c0, 0x24, 0x24, 0x0, 0x1438e40, 0x14363d0)
    /usr/local/Cellar/go/1.8.3/libexec/src/net/fd_unix.go:250 +0x1b7
net.(*conn).Read(0xc42000e010, 0xc4201000c0, 0x24, 0x24, 0x0, 0x0, 0x0)
    /usr/local/Cellar/go/1.8.3/libexec/src/net/net.go:181 +0x70
gopkg.in/mgo%2ev2.fill(0x143cce0, 0xc42000e010, 0xc4201000c0, 0x24, 0x24, 0x0, 0x0)
    /Users/erikmuttersbach/go/src/gopkg.in/mgo.v2/socket.go:535 +0x53
gopkg.in/mgo%2ev2.(*mongoSocket).readLoop(0xc420118000)
    /Users/erikmuttersbach/go/src/gopkg.in/mgo.v2/socket.go:551 +0x107
created by gopkg.in/mgo%2ev2.newSocket
    /Users/erikmuttersbach/go/src/gopkg.in/mgo.v2/socket.go:194 +0x259

goroutine 52 [select, 10 minutes]:
github.com/rwynn/gtm.FetchDocuments(0xc4200fe0a0, 0xc420063860, 0x131fa40, 0xc420100120, 0xc4201122a0, 0x0, 0x0)
    /Users/erikmuttersbach/go/src/github.com/rwynn/gtm/gtm.go:498 +0x3a7
created by github.com/rwynn/gtm.Start
    /Users/erikmuttersbach/go/src/github.com/rwynn/gtm/gtm.go:673 +0x5fc

goroutine 53 [semacquire, 10 minutes]:
sync.runtime_notifyListWait(0xc420150108, 0xc400000001)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/sema.go:298 +0x10b
sync.(*Cond).Wait(0xc4201500f8)
    /usr/local/Cellar/go/1.8.3/libexec/src/sync/cond.go:57 +0x89
gopkg.in/mgo%2ev2.(*Iter).Next(0xc4201500f0, 0x12c45e0, 0xc42007d590, 0x0)
    /Users/erikmuttersbach/go/src/gopkg.in/mgo.v2/session.go:3704 +0x9f
github.com/rwynn/gtm.TailOps(0xc4200fe0a0, 0xc420063860, 0xc42010a008, 0x1, 0x1, 0xc420110090, 0x0, 0x0)
    /Users/erikmuttersbach/go/src/github.com/rwynn/gtm/gtm.go:365 +0x1ea
created by github.com/rwynn/gtm.Start
    /Users/erikmuttersbach/go/src/github.com/rwynn/gtm/gtm.go:685 +0x8ea

rax    0xe
rbx    0x0
rcx    0x7fff5fbff3c0
rdx    0x186a0
rdi    0xf03
rsi    0x0
rbp    0x7fff5fbff3f8
rsp    0x7fff5fbff3c0
r8     0x43a260
r9     0x11
r10    0x653ef1e26c7c
r11    0x202
r12    0x1ad50d58ee7b
r13    0x1cbe784ba3b8
r14    0x14d44e02db750c00
r15    0x1
rip    0x10566a3
rflags 0x202
cs     0x7
fs     0x0
gs     0x0
exit status 2

最佳答案

问题出在垃圾收集器上,从堆栈跟踪可以看出(在向程序发送 SIGQUIT 之后):

goroutine 37 [running]:
runtime.systemstack_switch()
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/asm_amd64.s:281 fp=0xc420145608 sp=0xc420145600
runtime.gcStart(0x0, 0xc420366000)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/mgc.go:1010 +0x208 fp=0xc420145640 sp=0xc420145608
runtime.mallocgc(0x2500, 0x0, 0xc420106100, 0xc420145728)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/malloc.go:785 +0x491 fp=0xc4201456e0 sp=0xc420145640
runtime.rawstring(0x20e9, 0x0, 0x0, 0x0, 0x0, 0x0)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/string.go:237 +0x85 fp=0xc420145710 sp=0xc4201456e0
runtime.rawstringtmp(0x0, 0x20e9, 0x29, 0xc4201457c0, 0xc4200bdb80, 0xc420106ff0, 0xc420106120)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/string.go:107 +0x78 fp=0xc420145750 sp=0xc420145710
runtime.concatstrings(0x0, 0xc420145830, 0x2, 0x2, 0xc420106ff0, 0xc4201b8000)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/string.go:46 +0xf9 fp=0xc4201457e8 sp=0xc420145750
runtime.concatstring2(0x0, 0xc42051d900, 0x20c0, 0xc420106ff0, 0x29, 0xc420106ff0, 0x29)
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/string.go:55 +0x47 fp=0xc420145828 sp=0xc4201457e8
_/Users/erikmuttersbach/Projects/mongo2psql-go/db.GenerateInsert(0xc4200c9730, 0x4, 0xc4200c9650, 0x4, 0xc4200e1860, 0x1, 0x4, 0xc42006c400, 0x3, 0x4, ...)
    /Users/erikmuttersbach/Projects/mongo2psql-go/db/sql.go:27 +0x407 fp=0xc420145a10 sp=0xc420145828
main.writer(0xc4200e01a0, 0xc42015ccc0, 0xc420014320)
    /Users/erikmuttersbach/Projects/mongo2psql-go/main.go:112 +0x3b4 fp=0xc420145fc8 sp=0xc420145a10
runtime.goexit()
    /usr/local/Cellar/go/1.8.3/libexec/src/runtime/asm_amd64.s:2197 +0x1 fp=0xc420145fd0 sp=0xc420145fc8
created by main.main
    /Users/erikmuttersbach/Projects/mongo2psql-go/main.go:202 +0x135

runtime.gcStart(0x0, 0xc420366000) 行更具体地提示您 GC 想要启动但被阻止的事实。 GC 实际上被阻塞了,因为我开始了我的“永无止境”的 go 例程,如下所示:

func main() {
...
go writer()
go producer()

for {}
}

无限的 for 循环会阻塞 proc 并让垃圾收集器永远没有时间启动。

关于mongodb - 访问 map 时的 Goroutine block ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45269528/

有关mongodb - 访问 map 时的 Goroutine block的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  3. ruby - 续集在添加关联时访问many_to_many连接表 - 2

    我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以

  4. ruby-on-rails - 如何从过时的 TZInfo 标识符中获取 Rails TimeZone 名称? - 2

    已经有一个问题回答了如何将“America/Los_Angeles”转换为“PacificTime(US&Canada)”。但是我想将“美国/太平洋”和其他过时的时区转换为RailsTimeZone。我无法在图书馆中找到任何可以帮助我完成此任务的东西。 最佳答案 来自RailsActiveSupport::TimeZonedocs:TheversionofTZInfobundledwithActiveSupportonlyincludesthedefinitionsnecessarytosupportthezonesdefinedb

  5. ruby - 有没有办法从 ruby​​ case 语句中访问表达式? - 2

    我想从then子句中访问c​​ase语句表达式,即food="cheese"casefoodwhen"dip"then"carrotsticks"when"cheese"then"#{expr}crackers"else"mayo"end在这种情况下,expr是食物的当前值(value)。在这种情况下,我知道,我可以简单地访问变量food,但是在某些情况下,该值可能无法再访问(array.shift等)。除了将expr移出到局部变量然后访问它之外,是否有直接访问caseexpr值的方法?罗亚附注我知道这个具体示例很简单,只是一个示例场景。 最佳答案

  6. ruby - 从外部访问类的实例变量 - 2

    我理解(我认为)Ruby中类变量和类的实例变量之间的区别。我想知道如何从该类外部访问该类的实例变量。从内部(即在类方法中而不是实例方法中),它可以直接访问,但是从外部,有没有办法做MyClass.class.[@$#]variablename?我没有任何具体原因要这样做,只是学习Ruby并想知道是否可行。 最佳答案 classMyClass@my_class_instance_var="foo"class上述yield:>>foo我相信Arkku演示了如何从类外部访问类变量(@@),而不是类实例变量(@)。我从这篇文章中提取了上述内

  7. ruby-on-rails - 使用 HTTP.get_response 检索 Facebook 访问 token 时出现 Rails EOF 错误 - 2

    我试图在我的网站上实现使用Facebook登录功能,但在尝试从Facebook取回访问token时遇到障碍。这是我的代码:ifparams[:error_reason]=="user_denied"thenflash[:error]="TologinwithFacebook,youmustclick'Allow'toletthesiteaccessyourinformation"redirect_to:loginelsifparams[:code]thentoken_uri=URI.parse("https://graph.facebook.com/oauth/access_token

  8. ruby - 在 ruby​​ 中使用 .try 函数和 .map 函数 - 2

    我需要从json记录中获取一些值并像下面这样提取curr_json_doc['title']['genre'].map{|s|s['name']}.join(',')但对于某些记录,curr_json_doc['title']['genre']可以为空。所以我想对map和join()使用try函数。我试过如下curr_json_doc['title']['genre'].try(:map,{|s|s['name']}).try(:join,(','))但是没用。 最佳答案 你没有正确传递block。block被传递给参数括号外的方法

  9. ruby - 使用 Class.new 时访问外部范围 - 2

    是否有可能以某种方式访问​​Class.new范围内的a?a=5Class.new{defb;aend}.new.b#NameError:undefinedlocalvariableormethod`a'for#:0x007fa8b15e9af0>#:in`b' 最佳答案 即使@MarekLipka的回答是正确的——改变变量范围总是有风险的。这是可行的,因为每个block都带有创建它的上下文,因此您的局部变量a突然变得不那么局部了——它变成了一个“隐藏的”全局变量:a=5object=Class.new{define_method(

  10. ruby - 使用访问器方法即时创建对象 - 2

    使用散列定义的访问器方法动态创建对象的最简单方法是什么?例如,如果我有一个散列:{foo:"Foo",bar:"Bar"}我想要一个具有访问器方法foo、foo=、bar和bar=的对象,其初始值分别为"Foo"和"Bar"。我可以想到这样做:moduleObjectWithAccessordefself.newh;Struct.new(*h.keys).new(*h.values)endendo=ObjectWithAccessor.new(foo:"Foo",bar:"Bar")o.foo#=>"Foo"但是,我不需要它们的多个实例具有相同的特定键集,而是希望每次都使用可能不同的键

随机推荐