اشیاء سرویس در Ruby on Rails: بهترین روشها برای کدهای تمیز و قابل نگهداری

بسیاری از اوقات ما نیاز داریم که برنامه ROR خود را با دنیای خارج در میان بگذاریم، یا برای استفاده از سرویسی که اطلاعاتی را در اختیار ما قرار می دهد یا اینکه خودمان اطلاعاتی را ارسال کنیم.
و ما می توانیم این کار را به دو صورت انجام دهیم،
راه غارنشین، که با استفاده از HTTParty ساده است و شامل روشی برای تماس با API در کنترلر یا مدل ما است. این بدون هیچ مشکلی کار خواهد کرد، اگر فقط پروژه شما باشد نگهداری از آن سریع و شاید آسان است.
راه روبی روی ریل، به این ترتیب یک دایرکتوری برای سرویس های پروژه خود به نام “سرویس ها” در دایرکتوری “app” ایجاد می کنیم که در آن همه سرویس های خارجی یا API هایی را که پروژه ما مصرف می کند قرار می دهیم.
با استفاده از شیک بودن آن، قصد داریم با Chat GPT ارتباط برقرار کنیم.
باشه بریم سر کار
HTTParty را نصب کنید
gem 'httparty'
ما از HTTParty gem برای انجام تمام درخواست های HTTP خود استفاده می کنیم.
برای نصب آن کافیست کد بالا را به خود اضافه کنید Gemfile
و سپس اجرا کنید bundle install
.
پیکربندی سرویس
برای برقراری ارتباط con Chat GPT، باید یک API KEY یا TOKEN را در سرصفحه های درخواست خود ارسال کنیم، و برای ایمن نگه داشتن آن، آن را در فایل اعتبارنامه Rails ذخیره می کنیم.
با اجرای این دستور در ترمینال خود می توانید فایل اعتبارنامه را ویرایش کنید
EDITOR="nano" bin/rails credentials:edit
این فایل اعتبار شما را با استفاده از “nano” به عنوان ویرایشگر متن باز می کند.
هنگامی که آن را ویرایش کردید، خط پیکربندی زیر را اضافه کنید
chatgpt_api_key: YOUR-API-KEY
اگر از nano استفاده می کنید، لطفا ویرایشگر را ذخیره و ببندید، Ctrl + x
این ترفند را انجام خواهد داد.
شروع ساخت سرویس
ما می توانیم تمام خدمات خود را به عنوان ماژول در نظر بگیریم، ساختار زیر را ببینید
➜ chatgpt_service git:(master) tree app/services
app/services
├── chat_gpt
│ ├── data
│ │ ├── chat_completions_choice_item_data.rb
│ │ ├── completions_choice_item_data.rb
│ │ ├── models_item_data.rb
│ │ └── models_list_data.rb
│ ├── responses
│ │ ├── base_response.rb
│ │ ├── chat_completions_response.rb
│ │ ├── completions_response.rb
│ │ └── models_list_response.rb
│ └── service.rb
└── http_response.rb
4 directories, 10 files
ایده این است که یک ماژول برای محصور کردن API که میخواهیم مصرف کنیم و در داخل آن قرار دهیم Service
کلاس با تمام روش های درخواست یا ارسال اطلاعات.
در داخل پاسخها، کلاسهای خود را ذخیره میکنیم که مسئول مدیریت اطلاعات برگردانده شده توسط API هستند.
در صورتی که API دادههای پیچیده یا بسیاری از اشیاء را در داخل یکدیگر برگرداند، میتوانید از کلاسهای داده استفاده کنید، کلاسهایی که در داخل میتوانید مشاهده کنید. data
فهرست راهنما.
نگران کد نباشید، از آنجایی که من یک مخزن عمومی Github با این پروژه آماده کردم، به آن نگاهی بیندازید: https://github.com/paul-ot/ror_chatgpt_client
برای توضیح ساختار سرویس فوق می توان گفت که سرویس برای هر نقطه پایانی که استفاده می کنیم یک متد عمومی خواهد داشت، به عنوان مثال در مورد ما سه روش عمومی در سرویس داریم.completions
، chat_completions
و models_list
.
module ChatGpt
class Service
include HTTParty
base_uri 'https://api.openai.com/v1'
# ...
def completions(prompt, model = nil)
@model = model unless model.nil?
response = self.class.post(
COMPLETIONS_PATH,
body: completions_body(prompt),
headers: common_headers,
timeout: TIMEOUT_SECONDS
)
ChatGpt::Responses::CompletionsResponse.new(response)
end
def chat_completions(message, model = nil)
@model = model unless model.nil?
response = self.class.post(
COMPLETIONS_PATH,
body: chat_completions_body(message),
headers: common_headers,
timeout: TIMEOUT_SECONDS
)
ChatGpt::Responses::ChatCompletionsResponse.new(response)
end
def models_list
response = self.class.get(
MODELS_PATH,
headers: common_headers,
timeout: TIMEOUT_SECONDS
)
ChatGpt::Responses::ModelsListResponse.new(response)
end
# ...
end
end
هر متد پاسخ خود را برمیگرداند، به این معنی که هر متد یک پاسخ متفاوت، کلاس متفاوتی را که در داخل ذخیره میشود، برمیگرداند responses
دایرکتوری، ما این کار را از این طریق انجام می دهیم تا پاسخ ها را جدا کنیم و روش های مختلفی برای دریافت اطلاعات از پاسخ های JSON ایجاد کنیم.
به پاسخ ها توجه کنید
روش تکمیل:
def completions(prompt, model = nil)
# ...
ChatGpt::Responses::CompletionsResponse.new(response)
end
روش تکمیل چت:
def chat_completions(message, model = nil)
# ...
ChatGpt::Responses::ChatCompletionsResponse.new(response)
end
لیست مدل ها:
def models_list
# ...
ChatGpt::Responses::ModelsListResponse.new(response)
end
در اینجا هر کلاس پاسخ از a ارث می برد ChatGpt::Responses::BaseResponse
کلاسی که شامل متدهای رایج در بین پاسخ ها، پیام خطا است.
BaseResponse
module ChatGpt
module Responses
class BaseResponse < HttpResponse
def error_message
response_body.dig('error', 'message')
end
end
end
end
همزمان، ChatGpt::Responses::BaseResponse
ارث می برد از HttpResponse
کلاس، که شامل متدهای بسیار رایج است، برای مثال مسئول تعیین می کند که آیا پاسخ خوب بوده یا نه، و همچنین پاسخ را به شی JSON تبدیل می کند.
HttpResponse
class HttpResponse
attr_reader :response
def initialize(response)
@response = response
end
def response_body
response.body.present? ? JSON.parse(response.body) : {}
rescue JSON::ParserError
{}
end
def successful?
response.code.to_i == Rack::Utils::SYMBOL_TO_STATUS_CODE[:ok]
end
def failed?
!successful?
end
end
من این کار را در آن راه انجام دادم تا آن را قابل استفاده مجددتر کنم، بنابراین شما می توانید چندین سرویس با پاسخ های خاص خود داشته باشید و از این به ارث برسید. HttpResponse
کلاس
در مورد استفاده از این سرویس، بسیار ساده است
1- یک نمونه سرویس ایجاد کنید
> service = ChatGpt::Service.new
=> #<ChatGpt::Service:0x00007fccde2ed0d0 @model="gpt-3.5-turbo">
2- می توانید به صورت زیر سعی کنید لیست مدل های GPT را دریافت کنید
response = service.models_list
=> #<ChatGpt::Responses::ModelsListResponse:0x00007fccdd7d6390 @response=#<HTTParty::Response:0x7fccdd6c7ad0 parsed_response={"object"=>"list", "data"=>[{"id"=>"babbage", "object"=>"model", "created"=>1649358449, "owned_by"=>"openai", "permission"=>[{"id"=>"modelperm-49FUp5v084tBB49tC4z8LPH5", ...>
> response.items.count
=> 64
> response.items.first
=> #<ChatGpt::Data::ModelsItemData:0x00007fccde9cfa60 @params={"id"=>"babbage", "object"=>"model", "created"=>1649358449, "owned_by"=>"openai", "permission"=>[{"id"=>"modelperm-49FUp5v084tBB49tC4z8LPH5", "object"=>"model_permission", "created"=>1669085501, "allow_create_engine"=>false, "allow_sampling"=>true, "allow_logprobs"=>true, "allow_search_indices"=>false, "allow_view"=>true, "allow_fine_tuning"=>false, "organization"=>"*", "group"=>nil, "is_blocking"=>false}], "root"=>"babbage", "parent"=>nil}>
برای مثالهای بیشتر لطفاً به مخزن Github مراجعه کنید، یک فایل README.md به همراه آنها دارد.
امیدوارم چیز مفیدی ارائه داده باشم و امیدوارم توانسته باشم دیدگاه خود را در مورد استفاده از خدمات در Ruby on Rails توضیح دهم.
با تشکر برای خواندن!