nobetci_eczane_api 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +145 -0
- data/lib/nobetci_eczane_api/client.rb +270 -0
- data/lib/nobetci_eczane_api/error.rb +31 -0
- data/lib/nobetci_eczane_api/models/account_info.rb +31 -0
- data/lib/nobetci_eczane_api/models/api_response.rb +33 -0
- data/lib/nobetci_eczane_api/models/city.rb +31 -0
- data/lib/nobetci_eczane_api/models/district.rb +25 -0
- data/lib/nobetci_eczane_api/models/location.rb +31 -0
- data/lib/nobetci_eczane_api/models/pagination.rb +33 -0
- data/lib/nobetci_eczane_api/models/pharmacy.rb +91 -0
- data/lib/nobetci_eczane_api/models.rb +9 -0
- data/lib/nobetci_eczane_api/version.rb +5 -0
- data/lib/nobetci_eczane_api.rb +18 -0
- metadata +63 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 2a8facb665f17f0ead86695db01efb176796f9537ba5b97f9b79f1d053bb7e1d
|
|
4
|
+
data.tar.gz: 1cc991ef303279f538cd1ad1a640a561bfa43b0eff128705c6d0d838ea3a4300
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 90092b266b4d223db219cbf1a5f7bab60a2ce919cdfebc969cbfd3f6c8afd93544cb9a7a2431db53ca8bd13dde21f2a6a47f8084e96dfd692a6ac7cbdf40a518
|
|
7
|
+
data.tar.gz: c988498b9a0815059f1004c4a863013c31db5e246cd1251e93ca2e4bd5f7d28efd20bcac951c9017103c02a11e6587aed2d5b5dfcabba24916623d3fe697dfda
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mehmet Erdoğan (Eczaneler.ORG)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# NobetciEczaneApi Ruby SDK
|
|
2
|
+
|
|
3
|
+
Official Ruby Gem for **[Eczaneler.ORG](https://eczaneler.org)** v2 REST API.
|
|
4
|
+
|
|
5
|
+
Provides real-time duty pharmacies (nöbetçi eczaneler), coordinate-based nearby search, comprehensive city/district lists, and account status management for 81 provinces of Turkey and Northern Cyprus.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🚀 Kurulum (Installation)
|
|
10
|
+
|
|
11
|
+
Gemfile dosyanıza ekleyin:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
gem 'nobetci_eczane_api'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Ardından bundle komutunu çalıştırın:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
bundle install
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Veya doğrudan RubyGems üzerinden kurun:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
gem install nobetci_eczane_api
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## ⚡ Hızlı Başlangıç (Quickstart)
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
require 'nobetci_eczane_api'
|
|
35
|
+
|
|
36
|
+
# İstemciyi başlatın
|
|
37
|
+
client = NobetciEczaneApi::Client.new('SIZIN_API_ANAHTARINIZ')
|
|
38
|
+
|
|
39
|
+
# Türkiye geneli anlık nöbetçi eczaneleri listeleme
|
|
40
|
+
response = client.get_sentry_pharmacies(page: 1, limit: 10)
|
|
41
|
+
|
|
42
|
+
puts "Toplam Kayıt: #{response.pagination.total}"
|
|
43
|
+
response.data.each do |pharmacy|
|
|
44
|
+
puts "#{pharmacy.name} (#{pharmacy.city}/#{pharmacy.district}) - Tel: #{pharmacy.phone}"
|
|
45
|
+
puts "Adres: #{pharmacy.address}"
|
|
46
|
+
end
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 📖 Kullanım Örnekleri (Examples)
|
|
52
|
+
|
|
53
|
+
### 1. Şehir ve İlçe Bazlı Nöbetçi Eczaneler
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
# İstanbul (34) nöbetçi eczaneleri
|
|
57
|
+
istanbul_res = client.get_sentry_by_city(34, limit: 50)
|
|
58
|
+
istanbul_res.data.each do |p|
|
|
59
|
+
puts "#{p.name} - #{p.district} - Tel: #{p.phone}"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# İstanbul Kadıköy (34, 440) nöbetçi eczaneleri
|
|
63
|
+
kadikoy_res = client.get_sentry_by_district(34, 440)
|
|
64
|
+
kadikoy_res.data.each do |p|
|
|
65
|
+
puts "#{p.name} - #{p.address}"
|
|
66
|
+
end
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 2. GPS Koordinatlarına Göre En Yakın Eczaneler
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
# Kadıköy Moda koordinatlarına en yakın nöbetçi eczaneler
|
|
73
|
+
nearby = client.get_nearby_pharmacies(
|
|
74
|
+
latitude: 40.9876,
|
|
75
|
+
longitude: 29.0234,
|
|
76
|
+
is_sentry: true,
|
|
77
|
+
limit: 5
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
nearby.data.each do |p|
|
|
81
|
+
puts "#{p.name} (#{p.distance_km} km mesafede) - Tel: #{p.phone}"
|
|
82
|
+
end
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 3. İl ve İlçe Listeleri
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
# Tüm iller
|
|
89
|
+
cities = client.get_cities
|
|
90
|
+
cities.data.each do |city|
|
|
91
|
+
puts "[#{city.plate_code}] #{city.name} (ID: #{city.id})"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# İstanbul ilçeleri
|
|
95
|
+
districts = client.get_districts(34)
|
|
96
|
+
districts.data.each do |district|
|
|
97
|
+
puts "#{district.name} (ID: #{district.id})"
|
|
98
|
+
end
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 4. Hesap ve Kota Durumu
|
|
102
|
+
|
|
103
|
+
```ruby
|
|
104
|
+
account = client.get_account_info
|
|
105
|
+
info = account.data
|
|
106
|
+
|
|
107
|
+
puts "Paket: #{info.active_package}"
|
|
108
|
+
puts "Kalan Gün: #{info.days_left}"
|
|
109
|
+
puts "Hız Limiti: #{info.rate_limit}"
|
|
110
|
+
puts "İzinli IP'ler: #{info.allowed_ips.join(', ')}"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 5. Hata Yönetimi (Error Handling)
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
begin
|
|
117
|
+
client.get_sentry_by_city(34)
|
|
118
|
+
rescue NobetciEczaneApi::AuthenticationError => e
|
|
119
|
+
puts "Yetkilendirme Hatası: #{e.message}"
|
|
120
|
+
rescue NobetciEczaneApi::RateLimitError => e
|
|
121
|
+
puts "Hız Limiti Aşıldı (429): #{e.message}"
|
|
122
|
+
rescue NobetciEczaneApi::ValidationError => e
|
|
123
|
+
puts "Doğrulama Hatası: #{e.message}"
|
|
124
|
+
rescue NobetciEczaneApi::NetworkError => e
|
|
125
|
+
puts "Ağ Hatası: #{e.message}"
|
|
126
|
+
rescue NobetciEczaneApi::Error => e
|
|
127
|
+
puts "Genel API Hatası: #{e.message} (HTTP #{e.status_code})"
|
|
128
|
+
end
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 🛠️ Modeller ve Özellikler
|
|
134
|
+
|
|
135
|
+
- **`Pharmacy`**: `id`, `name`, `slug`, `city`, `city_id`, `district`, `district_id`, `address`, `address_description`, `phone`, `phone2`, `duty_start`, `duty_end`, `latitude`, `longitude`, `distance_km`, `is_sentry`, `source`
|
|
136
|
+
- **`City`**: `id`, `name`, `slug`, `plate_code`
|
|
137
|
+
- **`District`**: `id`, `name`, `slug`, `city_id`
|
|
138
|
+
- **`AccountInfo`**: `status`, `email`, `active_package`, `days_left`, `end_date`, `allowed_ips`, `rate_limit`
|
|
139
|
+
- **`Pagination`**: `total`, `per_page`, `current_page`, `total_pages`, `has_more`
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 📄 Lisans
|
|
144
|
+
|
|
145
|
+
MIT Lisansı ile korunmaktadır. Detaylar için [LICENSE](LICENSE) dosyasına bakabilirsiniz.
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "json"
|
|
5
|
+
require "uri"
|
|
6
|
+
require "openssl"
|
|
7
|
+
|
|
8
|
+
module NobetciEczaneApi
|
|
9
|
+
# Eczaneler.ORG v2 REST API resmi Ruby istemcisi.
|
|
10
|
+
class Client
|
|
11
|
+
DEFAULT_BASE_URL = "https://api.eczaneler.org/v2"
|
|
12
|
+
DEFAULT_TIMEOUT = 10
|
|
13
|
+
DEFAULT_OPEN_TIMEOUT = 5
|
|
14
|
+
USER_AGENT = "NobetciEczane-Ruby-SDK/#{NobetciEczaneApi::VERSION}"
|
|
15
|
+
|
|
16
|
+
attr_reader :api_key, :base_url, :timeout, :open_timeout
|
|
17
|
+
|
|
18
|
+
# Yeni bir API istemcisi örneği oluşturur.
|
|
19
|
+
#
|
|
20
|
+
# @param api_key [String] Eczaneler.ORG API Anahtarı
|
|
21
|
+
# @param base_url [String] Özel API taban adresi (varsayılan: https://api.eczaneler.org/v2)
|
|
22
|
+
# @param timeout [Integer] HTTP okuma zaman aşımı süresi (saniye)
|
|
23
|
+
# @param open_timeout [Integer] HTTP bağlantı zaman aşımı süresi (saniye)
|
|
24
|
+
def initialize(api_key, base_url: DEFAULT_BASE_URL, timeout: DEFAULT_TIMEOUT, open_timeout: DEFAULT_OPEN_TIMEOUT)
|
|
25
|
+
key = api_key.to_s.strip
|
|
26
|
+
raise AuthenticationError, "API anahtarı boş bırakılamaz." if key.empty?
|
|
27
|
+
|
|
28
|
+
@api_key = key
|
|
29
|
+
@base_url = base_url.to_s.chomp("/")
|
|
30
|
+
@timeout = timeout
|
|
31
|
+
@open_timeout = open_timeout
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Türkiye ve KKTC genelindeki tüm nöbetçi eczaneleri sayfalı listeler.
|
|
35
|
+
#
|
|
36
|
+
# @param page [Integer] Sayfa numarası (varsayılan: 1)
|
|
37
|
+
# @param limit [Integer] Sayfa başı kayıt sayısı (1 - 50 arası, varsayılan: 25)
|
|
38
|
+
# @return [ApiResponse] Nöbetçi eczaneleri içeren yanıt modeli
|
|
39
|
+
def get_sentry_pharmacies(page: 1, limit: 25)
|
|
40
|
+
valid_page = [1, page.to_i].max
|
|
41
|
+
valid_limit = [[1, limit.to_i].max, 50].min
|
|
42
|
+
send_request(:get, "/pharmacies/sentry-pharmacies", query: { page: valid_page, limit: valid_limit }) do |data|
|
|
43
|
+
(data || []).map { |item| Pharmacy.new(item) }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Belirli bir ile ait nöbetçi eczaneleri listeler (Örn: city_id: 34 - İstanbul).
|
|
48
|
+
#
|
|
49
|
+
# @param city_id [Integer] İl plaka / ID kodu (1 - 82)
|
|
50
|
+
# @param limit [Integer] Kayıt limiti (1 - 50 arası, varsayılan: 50)
|
|
51
|
+
# @return [ApiResponse] Nöbetçi eczaneleri içeren yanıt modeli
|
|
52
|
+
def get_sentry_by_city(city_id, limit: 50)
|
|
53
|
+
cid = city_id.to_i
|
|
54
|
+
raise ValidationError, "Geçersiz il ID numarası." if cid <= 0
|
|
55
|
+
|
|
56
|
+
valid_limit = [[1, limit.to_i].max, 50].min
|
|
57
|
+
send_request(:get, "/pharmacies/sentry-city-list/#{cid}", query: { limit: valid_limit }) do |data|
|
|
58
|
+
(data || []).map { |item| Pharmacy.new(item) }
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Belirli bir il ve ilçeye ait nöbetçi eczaneleri listeler (Örn: city_id: 34, district_id: 440).
|
|
63
|
+
#
|
|
64
|
+
# @param city_id [Integer] İl ID kodu
|
|
65
|
+
# @param district_id [Integer] İlçe ID kodu
|
|
66
|
+
# @param limit [Integer] Kayıt limiti (1 - 50 arası, varsayılan: 50)
|
|
67
|
+
# @return [ApiResponse] Nöbetçi eczaneleri içeren yanıt modeli
|
|
68
|
+
def get_sentry_by_district(city_id, district_id, limit: 50)
|
|
69
|
+
cid = city_id.to_i
|
|
70
|
+
did = district_id.to_i
|
|
71
|
+
raise ValidationError, "Geçersiz il veya ilçe ID numarası." if cid <= 0 || did <= 0
|
|
72
|
+
|
|
73
|
+
valid_limit = [[1, limit.to_i].max, 50].min
|
|
74
|
+
send_request(:get, "/pharmacies/sentry-district-list/#{cid}/#{did}", query: { limit: valid_limit }) do |data|
|
|
75
|
+
(data || []).map { |item| Pharmacy.new(item) }
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# GPS koordinatlarına (enlem ve boylam) göre en yakın eczaneleri mesafeye göre sıralı getirir.
|
|
80
|
+
#
|
|
81
|
+
# @param latitude [Float] Enlem (-90.0 .. 90.0)
|
|
82
|
+
# @param longitude [Float] Boylam (-180.0 .. 180.0)
|
|
83
|
+
# @param is_sentry [Boolean] Sadece nöbetçi olanlar mı getirilsin? (varsayılan: true)
|
|
84
|
+
# @param limit [Integer] Kayıt limiti (1 - 50 arası, varsayılan: 10)
|
|
85
|
+
# @return [ApiResponse] Mesafeye göre sıralı eczaneleri içeren yanıt modeli
|
|
86
|
+
def get_nearby_pharmacies(latitude:, longitude:, is_sentry: true, limit: 10)
|
|
87
|
+
lat = latitude.to_f
|
|
88
|
+
lon = longitude.to_f
|
|
89
|
+
raise ValidationError, "Geçersiz enlem veya boylam koordinatları." unless (-90.0..90.0).cover?(lat) && (-180.0..180.0).cover?(lon)
|
|
90
|
+
|
|
91
|
+
valid_limit = [[1, limit.to_i].max, 50].min
|
|
92
|
+
body = {
|
|
93
|
+
lat: lat,
|
|
94
|
+
lon: lon,
|
|
95
|
+
is_sentry: is_sentry ? 1 : 0,
|
|
96
|
+
limit: valid_limit
|
|
97
|
+
}
|
|
98
|
+
send_request(:post, "/pharmacies/pharmacies-nearby", body: body) do |data|
|
|
99
|
+
(data || []).map { |item| Pharmacy.new(item) }
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# 81 il ve KKTC il listesini getirir.
|
|
104
|
+
#
|
|
105
|
+
# @return [ApiResponse] İl listesini içeren yanıt modeli
|
|
106
|
+
def get_cities
|
|
107
|
+
send_request(:get, "/pharmacies/cities") do |data|
|
|
108
|
+
(data || []).map { |item| City.new(item) }
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Belirli bir ile ait tüm ilçelerin listesini getirir.
|
|
113
|
+
#
|
|
114
|
+
# @param city_id [Integer] İl ID kodu (Örn: 34 - İstanbul)
|
|
115
|
+
# @return [ApiResponse] İlçe listesini içeren yanıt modeli
|
|
116
|
+
def get_districts(city_id)
|
|
117
|
+
cid = city_id.to_i
|
|
118
|
+
raise ValidationError, "Geçersiz il ID numarası." if cid <= 0
|
|
119
|
+
|
|
120
|
+
send_request(:get, "/pharmacies/districts/#{cid}") do |data|
|
|
121
|
+
(data || []).map { |item| District.new(item) }
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# API anahtarınıza ait hesap durumu, kalan gün sayısı ve hız limitlerini getirir.
|
|
126
|
+
#
|
|
127
|
+
# @return [ApiResponse] Hesap bilgilerini içeren yanıt modeli
|
|
128
|
+
def get_account_info
|
|
129
|
+
send_request(:get, "/pharmacies/api-account-info") do |data|
|
|
130
|
+
data.is_a?(Hash) ? AccountInfo.new(data) : nil
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Güvenlik için izinli IP adresleri listesini (Whitelist) günceller.
|
|
135
|
+
#
|
|
136
|
+
# @param ips [String] Virgülle ayrılmış IP adresleri
|
|
137
|
+
# @return [ApiResponse] İşlem durumunu içeren yanıt modeli
|
|
138
|
+
def update_whitelist(ips)
|
|
139
|
+
ip_str = ips.to_s.strip
|
|
140
|
+
raise ValidationError, "IP adres listesi boş bırakılamaz." if ip_str.empty?
|
|
141
|
+
|
|
142
|
+
send_request(:post, "/update-whitelist", body: { ips: ip_str })
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# [Premium] Türkiye genelindeki tüm kayıtlı eczaneleri sayfalı listeler.
|
|
146
|
+
#
|
|
147
|
+
# @param page [Integer] Sayfa numarası (varsayılan: 1)
|
|
148
|
+
# @param limit [Integer] Sayfa başı limit (1 - 50 arası, varsayılan: 50)
|
|
149
|
+
# @return [ApiResponse] Eczane listesini içeren yanıt modeli
|
|
150
|
+
def get_all_pharmacies(page: 1, limit: 50)
|
|
151
|
+
valid_page = [1, page.to_i].max
|
|
152
|
+
valid_limit = [[1, limit.to_i].max, 50].min
|
|
153
|
+
send_request(:get, "/pharmacies/all-pharmacies", query: { page: valid_page, limit: valid_limit }) do |data|
|
|
154
|
+
(data || []).map { |item| Pharmacy.new(item) }
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# [Premium] Belirli bir ildeki tüm eczaneleri listeler.
|
|
159
|
+
#
|
|
160
|
+
# @param city_id [Integer] İl ID kodu
|
|
161
|
+
# @param limit [Integer] Kayıt limiti (1 - 50 arası, varsayılan: 50)
|
|
162
|
+
# @return [ApiResponse] İldeki tüm eczaneleri içeren yanıt modeli
|
|
163
|
+
def get_city_pharmacies(city_id, limit: 50)
|
|
164
|
+
cid = city_id.to_i
|
|
165
|
+
raise ValidationError, "Geçersiz il ID numarası." if cid <= 0
|
|
166
|
+
|
|
167
|
+
valid_limit = [[1, limit.to_i].max, 50].min
|
|
168
|
+
send_request(:get, "/pharmacies/city-pharmacies/#{cid}", query: { limit: valid_limit }) do |data|
|
|
169
|
+
(data || []).map { |item| Pharmacy.new(item) }
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# [Premium] Belirli bir ilçedeki tüm eczaneleri listeler.
|
|
174
|
+
#
|
|
175
|
+
# @param city_id [Integer] İl ID kodu
|
|
176
|
+
# @param district_id [Integer] İlçe ID kodu
|
|
177
|
+
# @param limit [Integer] Kayıt limiti (1 - 50 arası, varsayılan: 50)
|
|
178
|
+
# @return [ApiResponse] İlçedeki tüm eczaneleri içeren yanıt modeli
|
|
179
|
+
def get_district_pharmacies(city_id, district_id, limit: 50)
|
|
180
|
+
cid = city_id.to_i
|
|
181
|
+
did = district_id.to_i
|
|
182
|
+
raise ValidationError, "Geçersiz il veya ilçe ID numarası." if cid <= 0 || did <= 0
|
|
183
|
+
|
|
184
|
+
valid_limit = [[1, limit.to_i].max, 50].min
|
|
185
|
+
send_request(:get, "/pharmacies/district-pharmacies/#{cid}/#{did}", query: { limit: valid_limit }) do |data|
|
|
186
|
+
(data || []).map { |item| Pharmacy.new(item) }
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
private
|
|
191
|
+
|
|
192
|
+
def send_request(method, path, query: nil, body: nil, &block)
|
|
193
|
+
uri = URI.parse("#{@base_url}#{path}")
|
|
194
|
+
uri.query = URI.encode_www_form(query) if query && !query.empty?
|
|
195
|
+
|
|
196
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
197
|
+
http.use_ssl = (uri.scheme == "https")
|
|
198
|
+
http.read_timeout = @timeout
|
|
199
|
+
http.open_timeout = @open_timeout
|
|
200
|
+
|
|
201
|
+
req_class = method == :post ? Net::HTTP::Post : Net::HTTP::Get
|
|
202
|
+
request = req_class.new(uri.request_uri)
|
|
203
|
+
request["Accept"] = "application/json"
|
|
204
|
+
request["Content-Type"] = "application/json"
|
|
205
|
+
request["X-Api-Key"] = @api_key
|
|
206
|
+
request["User-Agent"] = USER_AGENT
|
|
207
|
+
|
|
208
|
+
request.body = JSON.generate(body) if body
|
|
209
|
+
|
|
210
|
+
begin
|
|
211
|
+
response = http.request(request)
|
|
212
|
+
rescue StandardError => e
|
|
213
|
+
raise NetworkError, "Ağ bağlantısı hatası: #{e.message}"
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
parse_response(response, &block)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def parse_response(response)
|
|
220
|
+
status_code = response.code.to_i
|
|
221
|
+
body_text = response.body.to_s
|
|
222
|
+
|
|
223
|
+
parsed_json = nil
|
|
224
|
+
begin
|
|
225
|
+
parsed_json = JSON.parse(body_text) if body_text && !body_text.empty?
|
|
226
|
+
rescue JSON::ParserError
|
|
227
|
+
parsed_json = nil
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# Hata durumları
|
|
231
|
+
if status_code >= 400 || (parsed_json.is_a?(Hash) && parsed_json["status"] == "error")
|
|
232
|
+
error_msg = parsed_json.is_a?(Hash) ? parsed_json["message"] : "HTTP #{status_code} hatası alındı."
|
|
233
|
+
error_msg ||= "Bilinmeyen API hatası (HTTP #{status_code})."
|
|
234
|
+
|
|
235
|
+
case status_code
|
|
236
|
+
when 401
|
|
237
|
+
raise AuthenticationError.new(error_msg, status_code: 401)
|
|
238
|
+
when 400, 422
|
|
239
|
+
raise ValidationError.new(error_msg, status_code: status_code)
|
|
240
|
+
when 404
|
|
241
|
+
raise NotFoundError.new(error_msg, status_code: 404)
|
|
242
|
+
when 429
|
|
243
|
+
raise RateLimitError.new(error_msg, status_code: 429)
|
|
244
|
+
when 500..599
|
|
245
|
+
raise ServerError.new(error_msg, status_code: status_code)
|
|
246
|
+
else
|
|
247
|
+
raise Error.new(error_msg, status_code: status_code)
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
raw_data = parsed_json.is_a?(Hash) ? parsed_json["data"] : nil
|
|
252
|
+
processed_data = block_given? ? yield(raw_data) : raw_data
|
|
253
|
+
|
|
254
|
+
pagination_obj = nil
|
|
255
|
+
if parsed_json.is_a?(Hash) && parsed_json["pagination"].is_a?(Hash)
|
|
256
|
+
pagination_obj = Pagination.new(parsed_json["pagination"])
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
ApiResponse.new(
|
|
260
|
+
status: parsed_json.is_a?(Hash) ? parsed_json["status"] : "success",
|
|
261
|
+
message: parsed_json.is_a?(Hash) ? parsed_json["message"] : nil,
|
|
262
|
+
data: processed_data,
|
|
263
|
+
pagination: pagination_obj,
|
|
264
|
+
total: parsed_json.is_a?(Hash) ? (parsed_json["total"] || pagination_obj&.total) : nil,
|
|
265
|
+
system_time: parsed_json.is_a?(Hash) ? parsed_json["system_time"] : nil,
|
|
266
|
+
raw_response: parsed_json
|
|
267
|
+
)
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NobetciEczaneApi
|
|
4
|
+
# Eczaneler.ORG API genel temel hata sınıfı
|
|
5
|
+
class Error < StandardError
|
|
6
|
+
attr_reader :status_code
|
|
7
|
+
|
|
8
|
+
def initialize(msg = nil, status_code: nil)
|
|
9
|
+
@status_code = status_code
|
|
10
|
+
super(msg)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# API anahtarı eksik veya geçersiz olduğunda fırlatılır (HTTP 401)
|
|
15
|
+
class AuthenticationError < Error; end
|
|
16
|
+
|
|
17
|
+
# Parametre doğrulama hatalarında fırlatılır (HTTP 400 / 422)
|
|
18
|
+
class ValidationError < Error; end
|
|
19
|
+
|
|
20
|
+
# Aranan kayıt bulunamadığında fırlatılır (HTTP 404)
|
|
21
|
+
class NotFoundError < Error; end
|
|
22
|
+
|
|
23
|
+
# Hız limiti aşıldığında fırlatılır (HTTP 429)
|
|
24
|
+
class RateLimitError < Error; end
|
|
25
|
+
|
|
26
|
+
# Sunucu kaynaklı hatalarda fırlatılır (HTTP 500+)
|
|
27
|
+
class ServerError < Error; end
|
|
28
|
+
|
|
29
|
+
# Ağ bağlantısı veya zaman aşımı hatalarında fırlatılır
|
|
30
|
+
class NetworkError < Error; end
|
|
31
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NobetciEczaneApi
|
|
4
|
+
# Hesap ve kota bilgileri modeli
|
|
5
|
+
class AccountInfo
|
|
6
|
+
attr_reader :status, :email, :active_package, :days_left, :end_date, :allowed_ips, :rate_limit
|
|
7
|
+
|
|
8
|
+
def initialize(attributes = {})
|
|
9
|
+
attrs = attributes || {}
|
|
10
|
+
@status = attrs["status"] || attrs[:status]
|
|
11
|
+
@email = attrs["email"] || attrs[:email]
|
|
12
|
+
@active_package = attrs["active_package"] || attrs[:active_package]
|
|
13
|
+
@days_left = (attrs["days_left"] || attrs[:days_left])&.to_i
|
|
14
|
+
@end_date = attrs["end_date"] || attrs[:end_date]
|
|
15
|
+
@allowed_ips = attrs["allowed_ips"] || attrs[:allowed_ips]
|
|
16
|
+
@rate_limit = attrs["rate_limit"] || attrs[:rate_limit]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def to_h
|
|
20
|
+
{
|
|
21
|
+
status: @status,
|
|
22
|
+
email: @email,
|
|
23
|
+
active_package: @active_package,
|
|
24
|
+
days_left: @days_left,
|
|
25
|
+
end_date: @end_date,
|
|
26
|
+
allowed_ips: @allowed_ips,
|
|
27
|
+
rate_limit: @rate_limit
|
|
28
|
+
}.compact
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NobetciEczaneApi
|
|
4
|
+
# API yanıt sarmalayıcısı (Envelope response)
|
|
5
|
+
class ApiResponse
|
|
6
|
+
attr_reader :status, :message, :data, :pagination, :total, :system_time, :raw_response
|
|
7
|
+
|
|
8
|
+
def initialize(status:, message: nil, data: nil, pagination: nil, total: nil, system_time: nil, raw_response: nil)
|
|
9
|
+
@status = status
|
|
10
|
+
@message = message
|
|
11
|
+
@data = data
|
|
12
|
+
@pagination = pagination
|
|
13
|
+
@total = total
|
|
14
|
+
@system_time = system_time
|
|
15
|
+
@raw_response = raw_response
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def success?
|
|
19
|
+
@status.to_s.casecmp("success").zero? || @status.to_s == "200" || @status.to_s.casecmp("ok").zero?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def to_h
|
|
23
|
+
{
|
|
24
|
+
status: @status,
|
|
25
|
+
message: @message,
|
|
26
|
+
data: @data.is_a?(Array) ? @data.map { |d| d.respond_to?(:to_h) ? d.to_h : d } : (@data.respond_to?(:to_h) ? @data.to_h : @data),
|
|
27
|
+
pagination: @pagination&.to_h,
|
|
28
|
+
total: @total,
|
|
29
|
+
system_time: @system_time
|
|
30
|
+
}.compact
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NobetciEczaneApi
|
|
4
|
+
# İl (Şehir) veri modeli
|
|
5
|
+
class City
|
|
6
|
+
attr_reader :id, :name, :slug, :plate, :plate_code, :districts
|
|
7
|
+
|
|
8
|
+
def initialize(attributes = {})
|
|
9
|
+
attrs = attributes || {}
|
|
10
|
+
@id = (attrs["id"] || attrs[:id]).to_i
|
|
11
|
+
@name = (attrs["name"] || attrs[:name]).to_s
|
|
12
|
+
@slug = attrs["slug"] || attrs[:slug]
|
|
13
|
+
@plate = (attrs["plate"] || attrs["plate_code"] || attrs[:plate] || attrs[:plate_code])&.to_i
|
|
14
|
+
@plate_code = @plate ? sprintf("%02d", @plate) : nil
|
|
15
|
+
|
|
16
|
+
dist_list = attrs["districts"] || attrs[:districts]
|
|
17
|
+
@districts = dist_list.is_a?(Array) ? dist_list.map { |d| District.new(d) } : nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_h
|
|
21
|
+
{
|
|
22
|
+
id: @id,
|
|
23
|
+
name: @name,
|
|
24
|
+
slug: @slug,
|
|
25
|
+
plate: @plate,
|
|
26
|
+
plate_code: @plate_code,
|
|
27
|
+
districts: @districts&.map(&:to_h)
|
|
28
|
+
}.compact
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NobetciEczaneApi
|
|
4
|
+
# İlçe veri modeli
|
|
5
|
+
class District
|
|
6
|
+
attr_reader :id, :city_id, :name, :slug
|
|
7
|
+
|
|
8
|
+
def initialize(attributes = {})
|
|
9
|
+
attrs = attributes || {}
|
|
10
|
+
@id = (attrs["id"] || attrs[:id]).to_i
|
|
11
|
+
@city_id = (attrs["city_id"] || attrs[:city_id])&.to_i
|
|
12
|
+
@name = (attrs["name"] || attrs[:name]).to_s
|
|
13
|
+
@slug = attrs["slug"] || attrs[:slug]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def to_h
|
|
17
|
+
{
|
|
18
|
+
id: @id,
|
|
19
|
+
city_id: @city_id,
|
|
20
|
+
name: @name,
|
|
21
|
+
slug: @slug
|
|
22
|
+
}.compact
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NobetciEczaneApi
|
|
4
|
+
# Eczane coğrafi konum ve harita yönlendirme bilgileri
|
|
5
|
+
class Location
|
|
6
|
+
attr_reader :latitude, :longitude, :google_maps_url, :yandex_maps_url, :apple_maps_url, :distance_meters, :distance_km
|
|
7
|
+
|
|
8
|
+
def initialize(attributes = {})
|
|
9
|
+
attrs = attributes || {}
|
|
10
|
+
@latitude = (attrs["latitude"] || attrs[:latitude])&.to_f
|
|
11
|
+
@longitude = (attrs["longitude"] || attrs[:longitude])&.to_f
|
|
12
|
+
@google_maps_url = attrs["google_maps_url"] || attrs[:google_maps_url]
|
|
13
|
+
@yandex_maps_url = attrs["yandex_maps_url"] || attrs[:yandex_maps_url]
|
|
14
|
+
@apple_maps_url = attrs["apple_maps_url"] || attrs[:apple_maps_url]
|
|
15
|
+
@distance_meters = (attrs["distance_meters"] || attrs[:distance_meters])&.to_f
|
|
16
|
+
@distance_km = (attrs["distance_km"] || attrs[:distance_km])&.to_f
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def to_h
|
|
20
|
+
{
|
|
21
|
+
latitude: @latitude,
|
|
22
|
+
longitude: @longitude,
|
|
23
|
+
google_maps_url: @google_maps_url,
|
|
24
|
+
yandex_maps_url: @yandex_maps_url,
|
|
25
|
+
apple_maps_url: @apple_maps_url,
|
|
26
|
+
distance_meters: @distance_meters,
|
|
27
|
+
distance_km: @distance_km
|
|
28
|
+
}.compact
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NobetciEczaneApi
|
|
4
|
+
# Sayfalama meta verileri modeli
|
|
5
|
+
class Pagination
|
|
6
|
+
attr_reader :total, :total_pages, :current_page, :per_page, :has_more, :has_prev_page, :prev_page, :next_page
|
|
7
|
+
|
|
8
|
+
def initialize(attributes = {})
|
|
9
|
+
attrs = attributes || {}
|
|
10
|
+
@total = attrs["total"] || attrs["total_records"] || attrs[:total] || attrs[:total_records]
|
|
11
|
+
@total_pages = attrs["total_pages"] || attrs[:total_pages]
|
|
12
|
+
@current_page = attrs["current_page"] || attrs[:current_page]
|
|
13
|
+
@per_page = attrs["per_page"] || attrs["limit"] || attrs[:per_page] || attrs[:limit]
|
|
14
|
+
@has_more = attrs.key?("has_more") ? attrs["has_more"] : (attrs.key?("has_next_page") ? attrs["has_next_page"] : attrs[:has_more])
|
|
15
|
+
@has_prev_page = attrs["has_prev_page"] || attrs[:has_prev_page]
|
|
16
|
+
@prev_page = attrs["prev_page"] || attrs[:prev_page]
|
|
17
|
+
@next_page = attrs["next_page"] || attrs[:next_page]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_h
|
|
21
|
+
{
|
|
22
|
+
total: @total,
|
|
23
|
+
total_pages: @total_pages,
|
|
24
|
+
current_page: @current_page,
|
|
25
|
+
per_page: @per_page,
|
|
26
|
+
has_more: @has_more,
|
|
27
|
+
has_prev_page: @has_prev_page,
|
|
28
|
+
prev_page: @prev_page,
|
|
29
|
+
next_page: @next_page
|
|
30
|
+
}.compact
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NobetciEczaneApi
|
|
4
|
+
# Eczane / Nöbetçi Eczane detay veri modeli
|
|
5
|
+
class Pharmacy
|
|
6
|
+
attr_reader :id, :name, :slug, :address, :address_description, :phone, :phone2,
|
|
7
|
+
:duty_start, :duty_end, :duty_note, :is_sentry, :working_hours,
|
|
8
|
+
:city_id, :city, :city_name, :city_slug, :district_id, :district,
|
|
9
|
+
:district_name, :district_slug, :locality, :neighborhood, :directions,
|
|
10
|
+
:latitude, :longitude, :map_link, :distance_km, :location
|
|
11
|
+
|
|
12
|
+
def initialize(attributes = {})
|
|
13
|
+
attrs = attributes || {}
|
|
14
|
+
@id = (attrs["id"] || attrs[:id]).to_i
|
|
15
|
+
@name = (attrs["name"] || attrs[:name]).to_s
|
|
16
|
+
@slug = attrs["slug"] || attrs[:slug]
|
|
17
|
+
@address = attrs["address"] || attrs[:address]
|
|
18
|
+
@address_description = attrs["address_description"] || attrs["address_desc"] || attrs[:address_description] || attrs[:address_desc]
|
|
19
|
+
@phone = attrs["phone"] || attrs[:phone]
|
|
20
|
+
@phone2 = attrs["phone2"] || attrs[:phone2]
|
|
21
|
+
@duty_start = attrs["duty_start"] || attrs[:duty_start]
|
|
22
|
+
@duty_end = attrs["duty_end"] || attrs[:duty_end]
|
|
23
|
+
@duty_note = attrs["duty_note"] || attrs[:duty_note]
|
|
24
|
+
|
|
25
|
+
sentry_val = attrs.key?("is_sentry") ? attrs["is_sentry"] : attrs[:is_sentry]
|
|
26
|
+
@is_sentry = [true, 1, "1", "true", "TRUE"].include?(sentry_val)
|
|
27
|
+
|
|
28
|
+
@working_hours = attrs["working_hours"] || attrs["workingHours"] || attrs[:working_hours] || attrs[:workingHours]
|
|
29
|
+
@city_id = (attrs["city_id"] || attrs[:city_id])&.to_i
|
|
30
|
+
@city = attrs["city"] || attrs["city_name"] || attrs[:city] || attrs[:city_name]
|
|
31
|
+
@city_name = @city
|
|
32
|
+
@city_slug = attrs["city_slug"] || attrs[:city_slug]
|
|
33
|
+
@district_id = (attrs["district_id"] || attrs[:district_id])&.to_i
|
|
34
|
+
@district = attrs["district"] || attrs["district_name"] || attrs[:district] || attrs[:district_name]
|
|
35
|
+
@district_name = @district
|
|
36
|
+
@district_slug = attrs["district_slug"] || attrs[:district_slug]
|
|
37
|
+
@locality = attrs["locality"] || attrs["neighborhood"] || attrs[:locality] || attrs[:neighborhood]
|
|
38
|
+
@neighborhood = @locality
|
|
39
|
+
@directions = attrs["directions"] || attrs[:directions]
|
|
40
|
+
@map_link = attrs["map_link"] || attrs[:map_link]
|
|
41
|
+
@distance_km = (attrs["distance_km"] || attrs[:distance_km])&.to_f
|
|
42
|
+
|
|
43
|
+
# Coordinates parsing
|
|
44
|
+
lat_val = attrs["latitude"] || attrs[:latitude]
|
|
45
|
+
lon_val = attrs["longitude"] || attrs[:longitude]
|
|
46
|
+
if (lat_val.nil? || lon_val.nil?) && (attrs["coordinates"] || attrs[:coordinates]).is_a?(Hash)
|
|
47
|
+
coords = attrs["coordinates"] || attrs[:coordinates]
|
|
48
|
+
lat_val ||= coords["lat"] || coords[:lat]
|
|
49
|
+
lon_val ||= coords["lng"] || coords[:lng]
|
|
50
|
+
end
|
|
51
|
+
@latitude = lat_val&.to_f
|
|
52
|
+
@longitude = lon_val&.to_f
|
|
53
|
+
|
|
54
|
+
loc_data = attrs["location"] || attrs[:location]
|
|
55
|
+
@location = loc_data.is_a?(Hash) ? Location.new(loc_data) : nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def to_h
|
|
59
|
+
{
|
|
60
|
+
id: @id,
|
|
61
|
+
name: @name,
|
|
62
|
+
slug: @slug,
|
|
63
|
+
address: @address,
|
|
64
|
+
address_description: @address_description,
|
|
65
|
+
phone: @phone,
|
|
66
|
+
phone2: @phone2,
|
|
67
|
+
duty_start: @duty_start,
|
|
68
|
+
duty_end: @duty_end,
|
|
69
|
+
duty_note: @duty_note,
|
|
70
|
+
is_sentry: @is_sentry,
|
|
71
|
+
working_hours: @working_hours,
|
|
72
|
+
city_id: @city_id,
|
|
73
|
+
city: @city,
|
|
74
|
+
city_name: @city_name,
|
|
75
|
+
city_slug: @city_slug,
|
|
76
|
+
district_id: @district_id,
|
|
77
|
+
district: @district,
|
|
78
|
+
district_name: @district_name,
|
|
79
|
+
district_slug: @district_slug,
|
|
80
|
+
locality: @locality,
|
|
81
|
+
neighborhood: @neighborhood,
|
|
82
|
+
directions: @directions,
|
|
83
|
+
latitude: @latitude,
|
|
84
|
+
longitude: @longitude,
|
|
85
|
+
map_link: @map_link,
|
|
86
|
+
distance_km: @distance_km,
|
|
87
|
+
location: @location&.to_h
|
|
88
|
+
}.compact
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "models/api_response"
|
|
4
|
+
require_relative "models/pagination"
|
|
5
|
+
require_relative "models/location"
|
|
6
|
+
require_relative "models/district"
|
|
7
|
+
require_relative "models/city"
|
|
8
|
+
require_relative "models/pharmacy"
|
|
9
|
+
require_relative "models/account_info"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "nobetci_eczane_api/version"
|
|
4
|
+
require_relative "nobetci_eczane_api/error"
|
|
5
|
+
require_relative "nobetci_eczane_api/models"
|
|
6
|
+
require_relative "nobetci_eczane_api/client"
|
|
7
|
+
|
|
8
|
+
module NobetciEczaneApi
|
|
9
|
+
class << self
|
|
10
|
+
# Kolay client başlatıcı
|
|
11
|
+
#
|
|
12
|
+
# @param api_key [String]
|
|
13
|
+
# @return [NobetciEczaneApi::Client]
|
|
14
|
+
def new(api_key, **options)
|
|
15
|
+
Client.new(api_key, **options)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: nobetci_eczane_api
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Mehmet Erdoğan
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-09-24 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: Türkiye (81 il) ve KKTC genelindeki tüm güncel nöbetçi eczaneler, il/ilçe
|
|
14
|
+
eczane listeleri ve GPS koordinat bazlı en yakın eczane sorguları için resmi Ruby
|
|
15
|
+
SDK kütüphanesi.
|
|
16
|
+
email:
|
|
17
|
+
- mehmeterdogan080@gmail.com
|
|
18
|
+
executables: []
|
|
19
|
+
extensions: []
|
|
20
|
+
extra_rdoc_files: []
|
|
21
|
+
files:
|
|
22
|
+
- LICENSE
|
|
23
|
+
- README.md
|
|
24
|
+
- lib/nobetci_eczane_api.rb
|
|
25
|
+
- lib/nobetci_eczane_api/client.rb
|
|
26
|
+
- lib/nobetci_eczane_api/error.rb
|
|
27
|
+
- lib/nobetci_eczane_api/models.rb
|
|
28
|
+
- lib/nobetci_eczane_api/models/account_info.rb
|
|
29
|
+
- lib/nobetci_eczane_api/models/api_response.rb
|
|
30
|
+
- lib/nobetci_eczane_api/models/city.rb
|
|
31
|
+
- lib/nobetci_eczane_api/models/district.rb
|
|
32
|
+
- lib/nobetci_eczane_api/models/location.rb
|
|
33
|
+
- lib/nobetci_eczane_api/models/pagination.rb
|
|
34
|
+
- lib/nobetci_eczane_api/models/pharmacy.rb
|
|
35
|
+
- lib/nobetci_eczane_api/version.rb
|
|
36
|
+
homepage: https://eczaneler.org/nobetci-eczane-api
|
|
37
|
+
licenses:
|
|
38
|
+
- MIT
|
|
39
|
+
metadata:
|
|
40
|
+
homepage_uri: https://eczaneler.org/nobetci-eczane-api
|
|
41
|
+
source_code_uri: https://github.com/mehmeterdogan/nobetci-eczane-api-ruby
|
|
42
|
+
changelog_uri: https://github.com/mehmeterdogan/nobetci-eczane-api-ruby/releases
|
|
43
|
+
rubygems_mfa_required: 'true'
|
|
44
|
+
post_install_message:
|
|
45
|
+
rdoc_options: []
|
|
46
|
+
require_paths:
|
|
47
|
+
- lib
|
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - ">="
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: 2.7.0
|
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
54
|
+
requirements:
|
|
55
|
+
- - ">="
|
|
56
|
+
- !ruby/object:Gem::Version
|
|
57
|
+
version: '0'
|
|
58
|
+
requirements: []
|
|
59
|
+
rubygems_version: 3.0.3.1
|
|
60
|
+
signing_key:
|
|
61
|
+
specification_version: 4
|
|
62
|
+
summary: Eczaneler.ORG v2 Nöbetçi Eczane REST API Resmi Ruby SDK
|
|
63
|
+
test_files: []
|