在给定用户输入的情况下,在哪里使用 HTTParty 调用 API?

Where to use HTTParty to call API given user input?

我正在尝试创建一个页面,该页面使用用户输入和使用 HTTParty 调用 Stack Exchange API 以获取 return 信息。我应该把下面的文件放在哪里,正如 HTTParty examples 之一中给出的那样,HTTParty 能够调用 StackExchange.new(user_input, 1) (其中 user_input 是前端提供的一些变量用户)?另外,有没有更通用的方法来调用 Rails 中的 API?我正在尝试从多个 API 和 return 中检索信息并将所有这些信息提供给用户。我可以将多个 get 请求合并为一个 class 吗?

代码:

class StackExchange
  include HTTParty
  base_uri 'api.stackexchange.com'

  def initialize(service, page)
    @options = { query: { site: service, page: page } }
  end

  def questions
    self.class.get("/2.2/questions", @options)
  end

  def users
    self.class.get("/2.2/users", @options)
  end
end

Where should I put the file below?

有几个选项。就个人而言,我会在 app 目录下创建一个 services 目录并将其放在那里。这样,rails 将自动加载文件。而且,我倾向于称它为 StackExchangeService(有点像 Controllers 最后总是有 Controller 的方式),但有些人不喜欢那样。

Is there a more general way to call APIs in Rails?

我不确定这是什么意思。

Could I combine multiple get requests into one class?

当然,您可以这样做:

class MultiFetchService

  attr_accessor :args

  class << self

    def call(args={})
      new(args).call
    end

  end  # Class Methods

  #==============================================================================================
  # Instance Methods
  #==============================================================================================

    def initialize(args)
      @args = args || {}
      assign_args
    end

    def call
      {
        stack_exchange: StackExchange(user_input, user_id)
        foo:            FooService(some, other, args)
      }
    end

  private

    def assign_args
      args.each do |k,v| 
        class_eval do 
          attr_accessor k
        end
        send("#{k}=",v)
      end
    end

end

你可以这样称呼它:

MultiFetchService.call(
  user_input: user_input, 
  user_id: 1, 
  some: :some, 
  other: :other, 
  args: :args
)

由于该 assign_args 方法,对于发送到 MultiFetchService 的每个密钥,您的服务中都会有一个方法(在本例中,user_inputuser_id, some, other, args)

在ruby中可以这样写:

#some_file.rb:

class A
  def initialize(x)
    @x = x
  end

  def dostuff
    puts "doing stuff with #{@x}"
  end
end

class B
  def go
    a = A.new(10)
    a.dostuff
  end
end

b = B.new
b.go

--output:--
doing stuff with 10

这表明一个文件中定义的 class 可以使用同一文件中定义的另一个 class。

您还可以将 classes 的名称更改为:

class StackExchange 
  def initialize(x)
    @x = x
  end

  def dostuff
    puts "doing stuff with #{@x}"
  end
end

class UsersController
  def go
    a = StackExchange.new(10)
    a.dostuff
  end
end

b = UsersController.new
b.go

--output:--
doing stuff with 10

在 rails 应用程序中,rails 负责执行以下行:

b = UsersController.new
b.go

当您创建到 controller#action 的路由,然后在浏览器中输入匹配路径时,rails 会创建 Controller 对象并调用操作方法。

您所要做的就是在控制器中编写操作方法 class:

class UsersController
  #You write this stuff:
  def go
    a = StackExchange.new(10)
    a.dostuff
  end
end

因此,对于一个非常简单的解决方案,您可以将您的 StackExchange class 粘贴到包含您的控制器 class 的文件的顶部,然后将其粘贴到任何控制器的 actions/methods 中,你可以打电话给 StackExchange.new.

但是,如果您的某些其他控制器中的操作(=方法)也需要使用 StackExchange class,那么不要将 StackExchange class 粘贴到每个控制器文件的顶部,您可以将 StackExchange class 放在一个名为 stack_exchange.rb 的文件中,然后将该文件放在所有控制器都可以访问的中央位置。

中央位置的一个选择是 lib/ 目录。您可以在 lib/ 目录中创建一个子目录,并将 stack_exchange.rb 文件放在那里:

lib/my_classes/stack_exchange.rb

然后在包含需要使用 StackExchange class 的控制器 class 之一的文件顶部,您只需要写:

require 'my_classes/stack_exchange'

这比必须将整个 StackExchange class 粘贴到文件顶部要短。

但是还有一个更简单的解决方案。在 ruby 中,一旦需要 class 就不需要再次要求,因此实际上不需要将 require 语句放在需要使用的每个控制器文件的顶部StackExchange class。相反,您可以使用配置文件来要求 StackExchange class 一次,然后您不必将要求语句粘贴到控制器文件的 any 顶部:

#config/initializers/my_require_classes.rb
require 'my_classes/stack_exchange'

请注意,如果您对配置文件进行了任何更改,则必须重新启动服务器。事实上,我发现 require 语句非常sticky,所以如果我更改 require 语句,有时我需要重新启动服务器。

StackExchange class 中心位置的另一个选择是在 app/ 目录中。在 rails 5 中,app/ 目录是自动加载的,这意味着 rails 为您创建了 require 语句,您可以愉快地使用 app/ 中的 classes =] 目录而不用担心 require 语句。