@@ -8,7 +8,22 @@ defmodule Helper.Utils do
8
8
9
9
import Helper.Validator.Guards , only: [ g_none_empty_str: 1 ]
10
10
11
- alias Helper.Cache
11
+ alias Helper . { Cache , Utils }
12
+
13
+ # Map utils
14
+ defdelegate map_key_stringify ( map ) , to: Utils.Map
15
+ defdelegate keys_to_atoms ( map ) , to: Utils.Map
16
+ defdelegate keys_to_strings ( map ) , to: Utils.Map
17
+ defdelegate camelize_map_key ( map ) , to: Utils.Map
18
+ defdelegate camelize_map_key ( map , opt ) , to: Utils.Map
19
+ defdelegate snake_map_key ( map ) , to: Utils.Map
20
+ defdelegate deep_merge ( left , right ) , to: Utils.Map
21
+ defdelegate map_atom_value ( attrs , opt ) , to: Utils.Map
22
+
23
+ # String Utils
24
+ defdelegate stringfy ( str ) , to: Utils.String
25
+ defdelegate count_words ( str ) , to: Utils.String
26
+ defdelegate str_occurence ( string , substr ) , to: Utils.String
12
27
13
28
def get_config ( section , key , app \\ :groupher_server )
14
29
@@ -87,125 +102,9 @@ defmodule Helper.Utils do
87
102
|> Absinthe.Resolution . put_result ( { :error , message: err_msg , code: ecode ( ) } )
88
103
end
89
104
90
- def map_key_stringify ( % { __struct__: _ } = map ) when is_map ( map ) do
91
- map = Map . from_struct ( map )
92
- map |> Enum . reduce ( % { } , fn { key , val } , acc -> Map . put ( acc , to_string ( key ) , val ) end )
93
- end
94
-
95
- def map_key_stringify ( map ) when is_map ( map ) do
96
- map |> Enum . reduce ( % { } , fn { key , val } , acc -> Map . put ( acc , to_string ( key ) , val ) end )
97
- end
98
-
99
- @ doc """
100
- see https://stackoverflow.com/a/61559842/4050784
101
- adjust it for map keys from atom to string
102
- """
103
- def keys_to_atoms ( json ) when is_map ( json ) do
104
- Map . new ( json , & reduce_keys_to_atoms / 1 )
105
- end
106
-
107
- def keys_to_atoms ( string ) when is_binary ( string ) , do: string
108
-
109
- def reduce_keys_to_atoms ( { key , val } ) when is_map ( val ) ,
110
- # do: {String.to_existing_atom(key), keys_to_atoms(val)}
111
- do: { String . to_atom ( key ) , keys_to_atoms ( val ) }
112
-
113
- def reduce_keys_to_atoms ( { key , val } ) when is_list ( val ) ,
114
- do: { String . to_atom ( key ) , Enum . map ( val , & keys_to_atoms ( & 1 ) ) }
115
-
116
- def reduce_keys_to_atoms ( { key , val } ) , do: { String . to_atom ( key ) , val }
117
-
118
- @ doc """
119
- see https://stackoverflow.com/a/61559842/4050784
120
- adjust it for map keys from atom to string
121
- """
122
- @ spec keys_to_strings ( map ) :: map
123
- def keys_to_strings ( json ) when is_map ( json ) do
124
- Map . new ( json , & reduce_keys_to_strings / 1 )
125
- end
126
-
127
- defp reduce_keys_to_strings ( { key , val } ) when is_map ( val ) ,
128
- do: { Atom . to_string ( key ) , keys_to_strings ( val ) }
129
-
130
- defp reduce_keys_to_strings ( { key , val } ) when is_list ( val ) ,
131
- do: { Atom . to_string ( key ) , Enum . map ( val , & keys_to_strings ( & 1 ) ) }
132
-
133
- defp reduce_keys_to_strings ( { key , val } ) , do: { Atom . to_string ( key ) , val }
134
-
135
- @ doc """
136
- Recursivly camelize the map keys
137
- usage: convert factory attrs to used for simu Graphql parmas
138
- """
139
- def camelize_map_key ( map , v_trans \\ :ignore ) do
140
- map_list =
141
- Enum . map ( map , fn { k , v } ->
142
- v =
143
- cond do
144
- is_datetime? ( v ) ->
145
- DateTime . to_iso8601 ( v )
146
-
147
- is_map ( v ) ->
148
- camelize_map_key ( safe_map ( v ) )
149
-
150
- is_binary ( v ) ->
151
- handle_camelize_value_trans ( v , v_trans )
152
-
153
- true ->
154
- v
155
- end
156
-
157
- map_to_camel ( { k , v } )
158
- end )
159
-
160
- Enum . into ( map_list , % { } )
161
- end
162
-
163
- defp handle_camelize_value_trans ( v , :ignore ) , do: v
164
- defp handle_camelize_value_trans ( v , :downcase ) , do: String . downcase ( v )
165
- defp handle_camelize_value_trans ( v , :upcase ) , do: String . upcase ( v )
166
-
167
- defp safe_map ( % { __struct__: _ } = map ) , do: Map . from_struct ( map )
168
- defp safe_map ( map ) , do: map
169
-
170
- defp map_to_camel ( { k , v } ) , do: { Recase . to_camel ( to_string ( k ) ) , v }
171
-
172
- @ spec snake_map_key ( map ) :: map
173
- def snake_map_key ( map ) do
174
- map_list =
175
- Enum . map ( map , fn { k , v } ->
176
- v =
177
- cond do
178
- is_datetime? ( v ) ->
179
- DateTime . to_iso8601 ( v )
180
-
181
- is_map ( v ) ->
182
- snake_map_key ( safe_map ( v ) )
183
-
184
- true ->
185
- v
186
- end
187
-
188
- { Recase . to_snake ( to_string ( k ) ) , v }
189
- end )
190
-
191
- Enum . into ( map_list , % { } )
192
- end
193
-
194
- def is_datetime? ( % DateTime { } ) , do: true
195
- def is_datetime? ( _ ) , do: false
196
-
197
- def deep_merge ( left , right ) do
198
- Map . merge ( left , right , & deep_resolve / 3 )
199
- end
200
-
201
105
def integerfy ( id ) when is_binary ( id ) , do: String . to_integer ( id )
202
106
def integerfy ( id ) , do: id
203
107
204
- def stringfy ( v ) when is_binary ( v ) , do: v
205
- def stringfy ( v ) when is_integer ( v ) , do: to_string ( v )
206
- def stringfy ( v ) when is_atom ( v ) , do: to_string ( v )
207
- def stringfy ( v ) , do: v
208
-
209
108
# TODO: enhance, doc
210
109
def repeat ( times , [ x ] ) when is_integer ( x ) , do: to_string ( for _ <- 1 .. times , do: x )
211
110
def repeat ( times , x ) , do: for ( _ <- 1 .. times , do: x )
@@ -220,58 +119,10 @@ defmodule Helper.Utils do
220
119
end )
221
120
end
222
121
223
- def map_atom_value ( attrs , :string ) do
224
- results =
225
- Enum . map ( attrs , fn { k , v } ->
226
- cond do
227
- v == true or v == false ->
228
- { k , v }
229
-
230
- is_atom ( v ) ->
231
- { k , v |> to_string ( ) |> String . downcase ( ) }
232
-
233
- true ->
234
- { k , v }
235
- end
236
- end )
237
-
238
- results |> Enum . into ( % { } )
239
- end
240
-
241
122
def empty_pagi_data do
242
123
% { entries: [ ] , total_count: 0 , page_size: 0 , total_pages: 1 , page_number: 1 }
243
124
end
244
125
245
- # Key exists in both maps, and both values are maps as well.
246
- # These can be merged recursively.
247
- # defp deep_resolve(_key, left = %{},right = %{}) do
248
- defp deep_resolve ( _key , % { } = left , % { } = right ) , do: deep_merge ( left , right )
249
-
250
- # Key exists in both maps, but at least one of the values is
251
- # NOT a map. We fall back to standard merge behavior, preferring
252
- # the value on the right.
253
- defp deep_resolve ( _key , _left , right ) , do: right
254
-
255
- @ doc """
256
- ["a", "b", "c", "c"] => %{"a" => 1, "b" => 1, "c" => 2}
257
- """
258
- def count_words ( words ) when is_list ( words ) do
259
- Enum . reduce ( words , % { } , & update_word_count / 2 )
260
- end
261
-
262
- defp update_word_count ( word , acc ) do
263
- Map . update ( acc , to_string ( word ) , 1 , & ( & 1 + 1 ) )
264
- end
265
-
266
- # see https://stackoverflow.com/a/49558074/4050784
267
- @ spec str_occurence ( String . t ( ) , String . t ( ) ) :: Integer . t ( )
268
- def str_occurence ( string , substr ) when is_binary ( string ) and is_binary ( substr ) do
269
- len = string |> String . split ( substr ) |> length ( )
270
- len - 1
271
- end
272
-
273
- def str_occurence ( _ , _ ) , do: "must be strings"
274
-
275
126
@ spec large_than ( String . t ( ) | Integer . t ( ) , Integer . t ( ) ) :: true | false
276
127
def large_than ( value , target ) when is_binary ( value ) and is_integer ( target ) do
277
128
String . length ( value ) >= target