cdek 0.3.12 → 0.3.13
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 +4 -4
- data/CHANGELOG.md +12 -0
- data/app/helpers/cdek/widget_helper.rb +18 -1
- data/lib/cdek/city_resolver.rb +40 -13
- data/lib/cdek/version.rb +1 -1
- data/lib/cdek.rb +7 -0
- data/lib/generators/cdek/install/templates/cdek_widget_controller.js +18 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ad8868fd8f6e296c1439f90772bf3c16e724d551ed095d6627250b8182bd486c
|
|
4
|
+
data.tar.gz: 29cabd18b3071e083f7ff8964bf8005f218da9985e35f6bcc80f030ac2677924
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bb520764c8df5cfa6b6b012d5e28e219c0a664124caaa07f58ce46ed7a56d079290d7bb8bf1e7f27c02805fd7faca35b051b4df60e3fb94a40112066ad258bf8
|
|
7
|
+
data.tar.gz: 9f832dae5fbb1443e52a9fb4f621b3a8052d308cc88d05d08089e6f2e423f27c815f4e9ecaac3f1c153ce828e470ee5947d528175b4d83c20f7b70eb05bd71ee
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.13
|
|
4
|
+
|
|
5
|
+
* Виджет ПВЗ больше не откатывает центр карты на Москву при выборе
|
|
6
|
+
не-московского города. `defaultLocation` передавался строкой-названием,
|
|
7
|
+
центр ставился асинхронным геокодингом и при сбое оставался дефолт
|
|
8
|
+
[37.62,55.75]. `CityResolver` теперь из одного кэшированного
|
|
9
|
+
`/location/cities` отдаёт и `code`, и координаты `[lng,lat]`;
|
|
10
|
+
`cdek_widget_tag` прокидывает `default_coords`, JS-контроллер использует
|
|
11
|
+
синхронную ветку без геокодинга. При отсутствии координат — fallback на
|
|
12
|
+
строку (нулевая регрессия). Перегенерировать контроллер:
|
|
13
|
+
`bin/rails generate cdek:install`.
|
|
14
|
+
|
|
3
15
|
## 0.3.9
|
|
4
16
|
|
|
5
17
|
* В `cdek.gemspec` добавлена development-зависимость `rake`, потому что
|
|
@@ -51,6 +51,7 @@ module Cdek
|
|
|
51
51
|
data = cdek_widget_data(
|
|
52
52
|
api_key: api_key.presence || ENV["YANDEX_MAPS_API_KEY"].to_s,
|
|
53
53
|
default_city: default_city,
|
|
54
|
+
default_coords: cdek_default_coords(default_city),
|
|
54
55
|
sender_city: sender_city,
|
|
55
56
|
sender_city_code: sender_city_code.to_s,
|
|
56
57
|
goods_json: JSON.generate(goods_payload),
|
|
@@ -91,7 +92,7 @@ module Cdek
|
|
|
91
92
|
|
|
92
93
|
# Хэш data-* атрибутов для Stimulus-контроллера cdek-widget.
|
|
93
94
|
# Ключи в snake_case — Rails сам конвертит "_" в "-" в HTML.
|
|
94
|
-
def cdek_widget_data(api_key:, default_city:, sender_city:, sender_city_code:,
|
|
95
|
+
def cdek_widget_data(api_key:, default_city:, default_coords:, sender_city:, sender_city_code:,
|
|
95
96
|
goods_json:,
|
|
96
97
|
service_path:, script_url:, modal_id:,
|
|
97
98
|
field_code:, field_name:, field_address:, field_city_code:,
|
|
@@ -102,6 +103,7 @@ module Cdek
|
|
|
102
103
|
cdek_widget_script_url_value: script_url,
|
|
103
104
|
cdek_widget_api_key_value: api_key,
|
|
104
105
|
cdek_widget_default_location_value: default_city,
|
|
106
|
+
cdek_widget_default_coords_value: default_coords,
|
|
105
107
|
cdek_widget_sender_city_value: sender_city,
|
|
106
108
|
cdek_widget_sender_city_code_value: sender_city_code,
|
|
107
109
|
cdek_widget_goods_value: goods_json,
|
|
@@ -115,6 +117,21 @@ module Cdek
|
|
|
115
117
|
}
|
|
116
118
|
end
|
|
117
119
|
|
|
120
|
+
# Координаты города «по умолчанию» строкой "долгота,широта" для data-атрибута
|
|
121
|
+
# (или "" если город пуст / не нашёлся / CDEK недоступен). JS-контроллер
|
|
122
|
+
# передаёт их виджету как defaultLocation = [lng, lat]; при пустой строке
|
|
123
|
+
# он откатывается на defaultLocation = название города (прежнее поведение).
|
|
124
|
+
def cdek_default_coords(city_name)
|
|
125
|
+
coords =
|
|
126
|
+
begin
|
|
127
|
+
Cdek.city_coordinates(city_name)
|
|
128
|
+
rescue Cdek::Error
|
|
129
|
+
nil
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
coords.is_a?(Array) && coords.length == 2 ? coords.join(",") : ""
|
|
133
|
+
end
|
|
134
|
+
|
|
118
135
|
# Путь до UMD-бандла виджета, вшитого в гем. Используется JS-контроллером
|
|
119
136
|
# для динамической загрузки скрипта по требованию.
|
|
120
137
|
def cdek_widget_asset_path
|
data/lib/cdek/city_resolver.rb
CHANGED
|
@@ -1,18 +1,27 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Cdek
|
|
4
|
-
# Resolves a human-readable city name to a CDEK city
|
|
4
|
+
# Resolves a human-readable city name to a CDEK city record — both the CDEK
|
|
5
|
+
# city code and the city coordinates — in one API-backed, cached lookup.
|
|
5
6
|
#
|
|
6
7
|
# This is intentionally small and API-backed: host applications can pass
|
|
7
8
|
# user-entered city names to the widget proxy without duplicating lookup
|
|
8
|
-
# logic or loading all delivery points for the whole country.
|
|
9
|
+
# logic or loading all delivery points for the whole country. The widget
|
|
10
|
+
# proxy uses #code to filter offices, while the widget helper uses
|
|
11
|
+
# #coordinates to center the map on the chosen city (passing coordinates to
|
|
12
|
+
# the JS widget instead of a name avoids a flaky async geocode that could
|
|
13
|
+
# otherwise drop the map back to its built-in default center).
|
|
9
14
|
class CityResolver
|
|
10
15
|
DEFAULT_COUNTRY_CODES = "RU"
|
|
11
16
|
CACHE_EXPIRES_IN = 43_200
|
|
12
17
|
|
|
13
18
|
class << self
|
|
14
19
|
def call(name, **options)
|
|
15
|
-
new(name, **options).
|
|
20
|
+
new(name, **options).code
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def coordinates(name, **options)
|
|
24
|
+
new(name, **options).coordinates
|
|
16
25
|
end
|
|
17
26
|
end
|
|
18
27
|
|
|
@@ -23,27 +32,45 @@ module Cdek
|
|
|
23
32
|
@cache = cache
|
|
24
33
|
end
|
|
25
34
|
|
|
26
|
-
|
|
27
|
-
|
|
35
|
+
# CDEK city code or nil when the city can't be resolved.
|
|
36
|
+
def code
|
|
37
|
+
city = cached_city
|
|
38
|
+
city.is_a?(Hash) ? (city["code"] || city[:code]) : nil
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# [longitude, latitude] (floats) or nil when coordinates are unknown.
|
|
42
|
+
# The order matches what the CDEK JS widget expects ([lng, lat]).
|
|
43
|
+
def coordinates
|
|
44
|
+
city = cached_city
|
|
45
|
+
lng = city_coordinate(city, "longitude")
|
|
46
|
+
lat = city_coordinate(city, "latitude")
|
|
47
|
+
|
|
48
|
+
(lng && lat) ? [lng, lat] : nil
|
|
28
49
|
end
|
|
29
50
|
|
|
30
51
|
private
|
|
31
52
|
|
|
32
53
|
attr_reader :name, :client, :country_codes, :cache
|
|
33
54
|
|
|
34
|
-
def
|
|
35
|
-
if
|
|
36
|
-
|
|
55
|
+
def cached_city
|
|
56
|
+
if normalized_name.empty?
|
|
57
|
+
nil
|
|
58
|
+
elsif cache
|
|
59
|
+
cache.fetch(cache_key, expires_in: CACHE_EXPIRES_IN) { fetch_city }
|
|
37
60
|
else
|
|
38
|
-
|
|
61
|
+
fetch_city
|
|
39
62
|
end
|
|
40
63
|
end
|
|
41
64
|
|
|
42
|
-
def
|
|
43
|
-
|
|
65
|
+
def fetch_city
|
|
66
|
+
Cdek.locations(client).find_city(normalized_name, country_codes: country_codes)
|
|
67
|
+
end
|
|
44
68
|
|
|
69
|
+
def city_coordinate(city, key)
|
|
45
70
|
if city.is_a?(Hash)
|
|
46
|
-
city[
|
|
71
|
+
raw = city.key?(key) ? city[key] : city[key.to_sym]
|
|
72
|
+
number = Float(raw, exception: false)
|
|
73
|
+
number&.finite? ? number : nil
|
|
47
74
|
end
|
|
48
75
|
end
|
|
49
76
|
|
|
@@ -52,7 +79,7 @@ module Cdek
|
|
|
52
79
|
end
|
|
53
80
|
|
|
54
81
|
def cache_key
|
|
55
|
-
["cdek", "
|
|
82
|
+
["cdek", "city", country_codes, normalized_name.downcase].join(":")
|
|
56
83
|
end
|
|
57
84
|
|
|
58
85
|
def default_cache
|
data/lib/cdek/version.rb
CHANGED
data/lib/cdek.rb
CHANGED
|
@@ -68,6 +68,13 @@ module Cdek
|
|
|
68
68
|
CityResolver.call(name, **options)
|
|
69
69
|
end
|
|
70
70
|
|
|
71
|
+
# Координаты [долгота, широта] города по пользовательскому названию
|
|
72
|
+
# (или nil, если город/координаты не нашлись). Нужны виджету, чтобы
|
|
73
|
+
# центрировать карту по выбранному городу координатами, а не названием.
|
|
74
|
+
def city_coordinates(name, **options)
|
|
75
|
+
CityResolver.coordinates(name, **options)
|
|
76
|
+
end
|
|
77
|
+
|
|
71
78
|
# Нормализованные подсказки городов для autocomplete в хост-приложении.
|
|
72
79
|
def city_suggestions(query, **options)
|
|
73
80
|
CitySuggestions.call(query, **options)
|
|
@@ -77,6 +77,7 @@ export default class extends Controller {
|
|
|
77
77
|
scriptUrl: String,
|
|
78
78
|
apiKey: String,
|
|
79
79
|
defaultLocation: { type: String, default: "Москва" },
|
|
80
|
+
defaultCoords: { type: String, default: "" },
|
|
80
81
|
senderCity: { type: String, default: "Москва" },
|
|
81
82
|
senderCityCode: { type: String, default: "" },
|
|
82
83
|
goods: { type: String, default: "" },
|
|
@@ -138,6 +139,22 @@ export default class extends Controller {
|
|
|
138
139
|
return url.pathname + url.search
|
|
139
140
|
}
|
|
140
141
|
|
|
142
|
+
// Координаты выбранного города ([lng, lat]) приходят из data-cdek-widget-default-coords-value.
|
|
143
|
+
// Передаём их виджету как defaultLocation — тогда он центрирует карту по
|
|
144
|
+
// координатам синхронно и не делает асинхронный геокодинг названия, который
|
|
145
|
+
// при сбое оставляет карту на встроенном дефолтном центре (Москве). Если
|
|
146
|
+
// координат нет (город не нашёлся / пусто) — откатываемся на название города.
|
|
147
|
+
_resolveDefaultLocation() {
|
|
148
|
+
const raw = this.defaultCoordsValue.trim()
|
|
149
|
+
const parts = raw === "" ? [] : raw.split(",")
|
|
150
|
+
const lng = Number(parts[0])
|
|
151
|
+
const lat = Number(parts[1])
|
|
152
|
+
|
|
153
|
+
return (parts.length === 2 && Number.isFinite(lng) && Number.isFinite(lat))
|
|
154
|
+
? [lng, lat]
|
|
155
|
+
: this.defaultLocationValue
|
|
156
|
+
}
|
|
157
|
+
|
|
141
158
|
_mountWidget() {
|
|
142
159
|
if (!this.hasRootTarget) return
|
|
143
160
|
if (typeof window.CDEKWidget !== "function") {
|
|
@@ -164,7 +181,7 @@ export default class extends Controller {
|
|
|
164
181
|
root: this.rootTarget.id,
|
|
165
182
|
servicePath: this._servicePath(),
|
|
166
183
|
apiKey: this.apiKeyValue,
|
|
167
|
-
defaultLocation: this.
|
|
184
|
+
defaultLocation: this._resolveDefaultLocation(),
|
|
168
185
|
from: from,
|
|
169
186
|
hideDeliveryOptions: { door: true, office: false },
|
|
170
187
|
lang: "rus",
|