sipo_mailer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 35eb2910aa10ad4d9aa5828307ccdd78f2a12c4e
4
+ data.tar.gz: d411582229cbb92b754b7d75b435a1132b1ce66c
5
+ SHA512:
6
+ metadata.gz: 2afa7e759d778b70e88cb9e25307afe648bc858e8de2ef07fc6536694b6560145e9cde8b76440cb28cc619299867bf6fd92401c5d3c632974fda99cf080cc91a
7
+ data.tar.gz: 0dd2339f220f4baadf8cd3a77220b54ab63c21b59447e98f0375abf059c944bd2b9da9a00a6e671beb8be6bd6f0520e71822dc34441d41394a525cb6622001b7
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ local_data/**
2
+ tmp/
3
+ docs/**
4
+ config/config.yml
5
+ **/**/.DS_Store
6
+ *.gem
data/.rubocop.yml ADDED
@@ -0,0 +1,64 @@
1
+ # This is the configuration used to check the rubocop source code.
2
+
3
+ inherit_from: .rubocop_todo.yml
4
+
5
+ AllCops:
6
+ Exclude:
7
+ - 'config/initializers/active_record_monkey_patch.rb'
8
+ CacheRootDirectory: './tmp'
9
+
10
+ Style/PredicateName:
11
+ Exclude:
12
+ - 'spec/support/feature_macros.rb'
13
+
14
+ Style/WordArray:
15
+ MinSize: 3
16
+
17
+ Style/SymbolProc:
18
+ IgnoredMethods: validate
19
+
20
+ Style/Documentation:
21
+ Enabled: false
22
+
23
+ Style/SingleLineBlockParams:
24
+ Enabled: false
25
+
26
+ Lint/UselessAccessModifier:
27
+ Enabled: false
28
+
29
+ Style/GuardClause:
30
+ Enabled: false
31
+
32
+ Style/PredicateName:
33
+ Enabled: false
34
+
35
+ Metrics/ModuleLength:
36
+ Max: 205
37
+
38
+ Metrics/LineLength:
39
+ Enabled: false
40
+
41
+ Style/EmptyMethod:
42
+ Enabled: false
43
+
44
+ Style/WordArray:
45
+ Enabled: false
46
+
47
+ # default zarovnavani vim
48
+ # ignoruje hash pokud jej pouzivame napr. jako parametr:
49
+ #
50
+ # mail to: 'info@uol.cz',
51
+ # subject: "Message: #{message.name}",
52
+ Layout/AlignHash:
53
+ EnforcedLastArgumentHashStyle: 'ignore_implicit'
54
+
55
+ # TODO vim jinak zarovnava pokud je metoda volana se zavorkou nebo bez
56
+ #
57
+ # create(:job,
58
+ # title: "Ucetni"
59
+ # VS
60
+ #
61
+ # create :job,
62
+ # title: "Ucetni"
63
+ Layout/AlignParameters:
64
+ Enabled: false
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,463 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2018-01-15 09:04:51 +0100 using RuboCop version 0.49.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 29
10
+ # Cop supports --auto-correct.
11
+ # Configuration parameters: Include, TreatCommentsAsGroupSeparators.
12
+ # Include: **/Gemfile, **/gems.rb
13
+ Bundler/OrderedGems:
14
+ Exclude:
15
+ - 'Gemfile'
16
+
17
+ # Offense count: 3
18
+ # Cop supports --auto-correct.
19
+ Layout/EmptyLinesAroundExceptionHandlingKeywords:
20
+ Exclude:
21
+ - 'app/models/document_template.rb'
22
+ - 'app/models/sales_invoice_import.rb'
23
+ - 'lib/tasks/import_currencies_exchange.rake'
24
+
25
+ # Offense count: 5
26
+ # Cop supports --auto-correct.
27
+ # Configuration parameters: AllowForAlignment, ForceEqualSignAlignment.
28
+ Layout/ExtraSpacing:
29
+ Exclude:
30
+ - 'app/controllers/users_controller.rb'
31
+ - 'app/models/contact.rb'
32
+ - 'app/models/offer.rb'
33
+ - 'app/models/sales_order.rb'
34
+
35
+ # Offense count: 1
36
+ # Cop supports --auto-correct.
37
+ # Configuration parameters: IndentationWidth.
38
+ Layout/IndentAssignment:
39
+ Exclude:
40
+ - 'app/services/cash_book_data_generator.rb'
41
+
42
+ # Offense count: 6
43
+ # Cop supports --auto-correct.
44
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
45
+ # SupportedStyles: symmetrical, new_line, same_line
46
+ Layout/MultilineArrayBraceLayout:
47
+ Exclude:
48
+ - 'app/controllers/application_controller.rb'
49
+ - 'spec/fixtures/payables.rb'
50
+ - 'spec/policies/sales_order_policy_spec.rb'
51
+
52
+ # Offense count: 57
53
+ # Cop supports --auto-correct.
54
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
55
+ # SupportedStyles: symmetrical, new_line, same_line
56
+ Layout/MultilineHashBraceLayout:
57
+ Enabled: false
58
+
59
+ # Offense count: 14
60
+ # Cop supports --auto-correct.
61
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
62
+ # SupportedStyles: symmetrical, new_line, same_line
63
+ Layout/MultilineMethodCallBraceLayout:
64
+ Exclude:
65
+ - 'app/services/accounting_records_pdf_generator.rb'
66
+ - 'spec/factories/uploaded_documents.rb'
67
+ - 'spec/models/address_spec.rb'
68
+ - 'spec/models/contact_spec.rb'
69
+ - 'spec/models/invitation_spec.rb'
70
+ - 'spec/services/paypal/statement_fetcher_spec.rb'
71
+ - 'spec/support/shared_examples/base_send_by.rb'
72
+
73
+ # Offense count: 113
74
+ # Cop supports --auto-correct.
75
+ # Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
76
+ # SupportedStyles: aligned, indented, indented_relative_to_receiver
77
+ Layout/MultilineMethodCallIndentation:
78
+ Enabled: false
79
+
80
+ # Offense count: 11
81
+ # Cop supports --auto-correct.
82
+ # Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
83
+ # SupportedStyles: aligned, indented
84
+ Layout/MultilineOperationIndentation:
85
+ Exclude:
86
+ - 'app/mailers/invitation_mailer.rb'
87
+ - 'app/models/address.rb'
88
+ - 'app/models/sales_invoice.rb'
89
+ - 'app/models/sales_invoice_item.rb'
90
+ - 'lib/tasks/intrastat_report.rake'
91
+ - 'lib/tenant_builder.rb'
92
+
93
+ # Offense count: 1
94
+ # Cop supports --auto-correct.
95
+ Layout/SpaceAroundKeyword:
96
+ Exclude:
97
+ - 'app/models/sales_order.rb'
98
+
99
+ # Offense count: 1
100
+ # Cop supports --auto-correct.
101
+ # Configuration parameters: AllowForAlignment.
102
+ Layout/SpaceAroundOperators:
103
+ Exclude:
104
+ - 'app/controllers/products/products_controller.rb'
105
+
106
+ # Offense count: 9
107
+ # Cop supports --auto-correct.
108
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
109
+ # SupportedStyles: require_no_space, require_space
110
+ Layout/SpaceInLambdaLiteral:
111
+ Exclude:
112
+ - 'app/models/accounting_record.rb'
113
+ - 'app/models/currency_pair.rb'
114
+ - 'app/models/logo.rb'
115
+ - 'app/models/message.rb'
116
+ - 'app/models/product.rb'
117
+ - 'config/environments/production.rb'
118
+
119
+ # Offense count: 18
120
+ # Cop supports --auto-correct.
121
+ Layout/SpaceInsidePercentLiteralDelimiters:
122
+ Exclude:
123
+ - 'app/models/concerns/after_document_issue_attributes.rb'
124
+ - 'app/models/my_bank_account.rb'
125
+ - 'app/models/my_company.rb'
126
+ - 'app/models/vat_rate.rb'
127
+ - 'config/initializers/assets.rb'
128
+ - 'spec/models/my_bank_account_spec.rb'
129
+ - 'spec/models/sales_order_spec.rb'
130
+
131
+ # Offense count: 5
132
+ Lint/AmbiguousBlockAssociation:
133
+ Exclude:
134
+ - 'spec/models/tenant_spec.rb'
135
+ - 'spec/services/document_template_manager_spec.rb'
136
+ - 'spec/services/paypal/balance_fetcher_spec.rb'
137
+ - 'spec/services/paypal/statement_fetcher_spec.rb'
138
+
139
+ # Offense count: 9
140
+ Lint/IneffectiveAccessModifier:
141
+ Exclude:
142
+ - 'app/data_objects/interval.rb'
143
+ - 'app/models/setting.rb'
144
+ - 'app/models/tenant.rb'
145
+ - 'app/services/public_id_guesser.rb'
146
+ - 'lib/expert_fantozzi_contract.rb'
147
+ - 'lib/ldap_user_finder.rb'
148
+
149
+ # Offense count: 1
150
+ # Cop supports --auto-correct.
151
+ Lint/UnifiedInteger:
152
+ Exclude:
153
+ - 'spec/support/rspec_matchers/have_row.rb'
154
+
155
+ # Offense count: 1
156
+ # Cop supports --auto-correct.
157
+ Lint/UnneededDisable:
158
+ Exclude:
159
+ - 'app/queries/query/budget_by_days.rb'
160
+
161
+ # Offense count: 1
162
+ # Cop supports --auto-correct.
163
+ Lint/UnneededSplatExpansion:
164
+ Exclude:
165
+ - 'app/api/v1/sales_orders.rb'
166
+
167
+ # Offense count: 272
168
+ Metrics/AbcSize:
169
+ Max: 169
170
+
171
+ # Offense count: 607
172
+ # Configuration parameters: CountComments, ExcludedMethods.
173
+ Metrics/BlockLength:
174
+ Max: 1260
175
+
176
+ # Offense count: 42
177
+ # Configuration parameters: CountComments.
178
+ Metrics/ClassLength:
179
+ Max: 302
180
+
181
+ # Offense count: 56
182
+ Metrics/CyclomaticComplexity:
183
+ Max: 17
184
+
185
+ # Offense count: 219
186
+ # Configuration parameters: CountComments.
187
+ Metrics/MethodLength:
188
+ Max: 125
189
+
190
+ # Offense count: 6
191
+ # Configuration parameters: CountKeywordArgs.
192
+ Metrics/ParameterLists:
193
+ Max: 7
194
+
195
+ # Offense count: 37
196
+ Metrics/PerceivedComplexity:
197
+ Max: 17
198
+
199
+ # Offense count: 1
200
+ # Cop supports --auto-correct.
201
+ Performance/Casecmp:
202
+ Exclude:
203
+ - 'app/models/product.rb'
204
+
205
+ # Offense count: 5
206
+ # Cop supports --auto-correct.
207
+ Performance/RangeInclude:
208
+ Exclude:
209
+ - 'app/models/accounting_period.rb'
210
+ - 'app/validators/vatin_validator.rb'
211
+ - 'lib/tasks/intrastat_report.rake'
212
+
213
+ # Offense count: 1
214
+ # Cop supports --auto-correct.
215
+ Performance/RedundantBlockCall:
216
+ Exclude:
217
+ - 'spec/support/database_helper.rb'
218
+
219
+ # Offense count: 8
220
+ # Cop supports --auto-correct.
221
+ # Configuration parameters: MaxKeyValuePairs.
222
+ Performance/RedundantMerge:
223
+ Exclude:
224
+ - 'app/api/v1/demands_for_payment.rb'
225
+ - 'app/api/v1/my_bank_accounts.rb'
226
+ - 'app/api/v1/pagination_helpers.rb'
227
+ - 'app/services/frontend_modifier_symbolizer.rb'
228
+ - 'app/services/modifier_symbolizer.rb'
229
+ - 'spec/api/v1/contacts_spec.rb'
230
+
231
+ # Offense count: 3
232
+ # Cop supports --auto-correct.
233
+ Security/YAMLLoad:
234
+ Exclude:
235
+ - 'config/application.rb'
236
+ - 'lib/ldap_user_finder.rb'
237
+ - 'lib/tasks/migrate.rake'
238
+
239
+ # Offense count: 6
240
+ # Cop supports --auto-correct.
241
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
242
+ # SupportedStyles: prefer_alias, prefer_alias_method
243
+ Style/Alias:
244
+ Exclude:
245
+ - 'app/models/search/sales_order.rb'
246
+ - 'app/models/user_permission.rb'
247
+ - 'app/models/user_permissions_collection.rb'
248
+ - 'app/services/subject_to_vat_specification.rb'
249
+
250
+ # Offense count: 10
251
+ Style/AsciiComments:
252
+ Exclude:
253
+ - 'app/controllers/application_controller.rb'
254
+ - 'app/models/currency.rb'
255
+ - 'app/workers/cost_revenue_worker_base.rb'
256
+ - 'spec/factories/reports.rb'
257
+ - 'spec/models/my_bank_account_spec.rb'
258
+ - 'spec/models/vat_rate_spec.rb'
259
+
260
+ # Offense count: 6
261
+ # Cop supports --auto-correct.
262
+ # Configuration parameters: EnforcedStyle, SupportedStyles, SingleLineConditionsOnly, IncludeTernaryExpressions.
263
+ # SupportedStyles: assign_to_condition, assign_inside_condition
264
+ Style/ConditionalAssignment:
265
+ Exclude:
266
+ - 'app/helpers/invoice_helper.rb'
267
+ - 'app/models/address.rb'
268
+ - 'app/models/product.rb'
269
+ - 'app/services/cash_book_data_generator.rb'
270
+ - 'app/validators/bank_account_or_iban_validator.rb'
271
+ - 'app/validators/sales_invoice_validator.rb'
272
+
273
+ # Offense count: 4
274
+ # Cop supports --auto-correct.
275
+ Style/EmptyCaseCondition:
276
+ Exclude:
277
+ - 'app/decorators/concerns/document_addresses.rb'
278
+ - 'app/services/subject_to_vat_specification.rb'
279
+ - 'app/validators/linked_doc_validator.rb'
280
+
281
+ # Offense count: 1294
282
+ # Cop supports --auto-correct.
283
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
284
+ # SupportedStyles: when_needed, always, never
285
+ Style/FrozenStringLiteralComment:
286
+ Enabled: false
287
+
288
+ # Offense count: 2
289
+ Style/IdenticalConditionalBranches:
290
+ Exclude:
291
+ - 'lib/tasks/export_accounting_records_to_pdf.rake'
292
+
293
+ # Offense count: 10
294
+ Style/IfInsideElse:
295
+ Exclude:
296
+ - 'app/controllers/logos_controller.rb'
297
+ - 'app/models/currency_pair.rb'
298
+ - 'app/models/default_attrs/purchase_invoice.rb'
299
+ - 'app/models/purchase_invoice_item.rb'
300
+ - 'app/queries/query/receivables_core.rb'
301
+ - 'app/services/currency_exchange.rb'
302
+ - 'app/services/subject_to_vat_specification.rb'
303
+ - 'app/validators/vatin_validator.rb'
304
+
305
+ # Offense count: 1
306
+ # Cop supports --auto-correct.
307
+ # Configuration parameters: MaxLineLength.
308
+ Style/IfUnlessModifier:
309
+ Exclude:
310
+ - 'app/validators/vatin_validator.rb'
311
+
312
+ # Offense count: 2
313
+ # Cop supports --auto-correct.
314
+ # Configuration parameters: InverseMethods, InverseBlocks.
315
+ Style/InverseMethods:
316
+ Exclude:
317
+ - 'app/models/sales_invoice.rb'
318
+ - 'app/validators/bank_symbol_validator.rb'
319
+
320
+ # Offense count: 2
321
+ Style/MultilineBlockChain:
322
+ Exclude:
323
+ - 'app/controllers/contracts/most_used_controller.rb'
324
+
325
+ # Offense count: 1
326
+ # Cop supports --auto-correct.
327
+ Style/MultilineIfModifier:
328
+ Exclude:
329
+ - 'app/services/currency_exchange.rb'
330
+
331
+ # Offense count: 2
332
+ Style/MultipleComparison:
333
+ Exclude:
334
+ - 'app/helpers/report_helper.rb'
335
+ - 'lib/core_ext/date.rb'
336
+
337
+ # Offense count: 134
338
+ # Cop supports --auto-correct.
339
+ Style/MutableConstant:
340
+ Enabled: false
341
+
342
+ # Offense count: 4
343
+ # Cop supports --auto-correct.
344
+ Style/NestedParenthesizedCalls:
345
+ Exclude:
346
+ - 'app/controllers/contacts_controller.rb'
347
+ - 'app/decorators/concerns/document_addresses.rb'
348
+ - 'app/models/purchase_order.rb'
349
+ - 'app/services/logo_upload.rb'
350
+
351
+ # Offense count: 1
352
+ # Cop supports --auto-correct.
353
+ # Configuration parameters: EnforcedOctalStyle, SupportedOctalStyles.
354
+ # SupportedOctalStyles: zero_with_o, zero_only
355
+ Style/NumericLiteralPrefix:
356
+ Exclude:
357
+ - 'spec/factories/bank_payments.rb'
358
+
359
+ # Offense count: 14
360
+ # Cop supports --auto-correct.
361
+ # Configuration parameters: Strict.
362
+ Style/NumericLiterals:
363
+ MinDigits: 11
364
+
365
+ # Offense count: 35
366
+ # Cop supports --auto-correct.
367
+ # Configuration parameters: AutoCorrect, EnforcedStyle, SupportedStyles.
368
+ # SupportedStyles: predicate, comparison
369
+ Style/NumericPredicate:
370
+ Enabled: false
371
+
372
+ # Offense count: 1
373
+ # Cop supports --auto-correct.
374
+ # Configuration parameters: AllowSafeAssignment.
375
+ Style/ParenthesesAroundCondition:
376
+ Exclude:
377
+ - 'app/models/default_attrs/purchase_invoice.rb'
378
+
379
+ # Offense count: 101
380
+ # Cop supports --auto-correct.
381
+ # Configuration parameters: PreferredDelimiters.
382
+ Style/PercentLiteralDelimiters:
383
+ Enabled: false
384
+
385
+ # Offense count: 10
386
+ # Cop supports --auto-correct.
387
+ Style/RedundantParentheses:
388
+ Exclude:
389
+ - 'app/controllers/application_controller.rb'
390
+ - 'app/models/cash_book_report.rb'
391
+ - 'app/models/concerns/cost_revenue_report_base.rb'
392
+ - 'app/models/contract_report.rb'
393
+ - 'app/models/costs_by_contacts_report.rb'
394
+ - 'app/models/purchase_invoice_item.rb'
395
+ - 'app/queries/query/receivables_core.rb'
396
+ - 'spec/models/external_notification_spec.rb'
397
+
398
+ # Offense count: 3
399
+ # Cop supports --auto-correct.
400
+ # Configuration parameters: AllowMultipleReturnValues.
401
+ Style/RedundantReturn:
402
+ Exclude:
403
+ - 'app/data_objects/interval.rb'
404
+
405
+ # Offense count: 44
406
+ # Cop supports --auto-correct.
407
+ # Configuration parameters: ConvertCodeThatCanStartToReturnNil.
408
+ Style/SafeNavigation:
409
+ Enabled: false
410
+
411
+ # Offense count: 55
412
+ # Cop supports --auto-correct.
413
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
414
+ # SupportedStyles: only_raise, only_fail, semantic
415
+ Style/SignalException:
416
+ Enabled: false
417
+
418
+ # Offense count: 1
419
+ # Cop supports --auto-correct.
420
+ # Configuration parameters: EnforcedStyle, SupportedStyles, ConsistentQuotesInMultiline.
421
+ # SupportedStyles: single_quotes, double_quotes
422
+ Style/StringLiterals:
423
+ Exclude:
424
+ - 'spec/models/invitation_spec.rb'
425
+
426
+ # Offense count: 160
427
+ # Cop supports --auto-correct.
428
+ # Configuration parameters: EnforcedStyle, MinSize, SupportedStyles.
429
+ # SupportedStyles: percent, brackets
430
+ Style/SymbolArray:
431
+ Enabled: false
432
+
433
+ # Offense count: 5
434
+ # Cop supports --auto-correct.
435
+ Style/UnneededInterpolation:
436
+ Exclude:
437
+ - 'app/helpers/select_helper.rb'
438
+ - 'app/mailers/notification_mailer.rb'
439
+ - 'app/models/user.rb'
440
+ - 'app/services/frontend_params_normalizer.rb'
441
+ - 'spec/features/sales/orders/order_spec.rb'
442
+
443
+ # Offense count: 43
444
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
445
+ # SupportedStyles: snake_case, normalcase, non_integer
446
+ Style/VariableNumber:
447
+ Exclude:
448
+ - 'spec/api/v1/sales_orders_spec.rb'
449
+ - 'spec/features/purchase/invoices_spec.rb'
450
+ - 'spec/features/purchase/petty_cash_disburstment_spec.rb'
451
+ - 'spec/features/sales/orders/order_spec.rb'
452
+ - 'spec/features/sales/petty_cash_income_spec.rb'
453
+ - 'spec/queries/query/cash_book_spec.rb'
454
+ - 'spec/queries/query/contracts_costs_report_spec.rb'
455
+ - 'spec/queries/query/contracts_revenues_report_spec.rb'
456
+ - 'spec/services/cash_book_data_generator_spec.rb'
457
+ - 'spec/services/generate_order_from_confirmed_payments_service_spec.rb'
458
+
459
+ # Offense count: 1
460
+ # Cop supports --auto-correct.
461
+ Style/YodaCondition:
462
+ Exclude:
463
+ - 'app/services/subject_to_vat_specification.rb'
data/Dockerfile ADDED
@@ -0,0 +1,18 @@
1
+ FROM landovsky/ruby:2.4.0
2
+
3
+ ## alias musi byt v .bashrc
4
+ RUN echo '---' > ~/.gemrc \
5
+ && echo 'gem: --no-document' >> ~/.gemrc
6
+
7
+ ENV APPDIR /sipo
8
+ RUN mkdir $APPDIR
9
+ WORKDIR $APPDIR
10
+
11
+ ENV BUNDLE_JOBS=2 \
12
+ BUNDLE_PATH=/gems \
13
+ # do GEM_PATHS se musí přidat cesta z gem_storu
14
+ GEM_PATH=$GEM_PATH:/gems \
15
+ #PATH=$PATH:/gems/bin:/sipo/bin
16
+ PATH=$PATH:/gems/bin
17
+
18
+ ADD . .
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in blender.gemspec
4
+ gem 'pry'
5
+ gem 'rspec'
6
+ gem 'bundler'
7
+ gem 'mail'
8
+ gem 'dry-types'
9
+ gem 'dry-struct'
10
+ gem 'dry-validation'
data/Gemfile.lock ADDED
@@ -0,0 +1,75 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ coderay (1.1.2)
5
+ concurrent-ruby (1.0.5)
6
+ diff-lcs (1.3)
7
+ dry-configurable (0.7.0)
8
+ concurrent-ruby (~> 1.0)
9
+ dry-container (0.6.0)
10
+ concurrent-ruby (~> 1.0)
11
+ dry-configurable (~> 0.1, >= 0.1.3)
12
+ dry-core (0.4.2)
13
+ concurrent-ruby (~> 1.0)
14
+ dry-equalizer (0.2.0)
15
+ dry-logic (0.4.2)
16
+ dry-container (~> 0.2, >= 0.2.6)
17
+ dry-core (~> 0.2)
18
+ dry-equalizer (~> 0.2)
19
+ dry-struct (0.4.0)
20
+ dry-core (~> 0.4, >= 0.4.1)
21
+ dry-equalizer (~> 0.2)
22
+ dry-types (~> 0.12, >= 0.12.2)
23
+ ice_nine (~> 0.11)
24
+ dry-types (0.12.2)
25
+ concurrent-ruby (~> 1.0)
26
+ dry-configurable (~> 0.1)
27
+ dry-container (~> 0.3)
28
+ dry-core (~> 0.2, >= 0.2.1)
29
+ dry-equalizer (~> 0.2)
30
+ dry-logic (~> 0.4, >= 0.4.2)
31
+ inflecto (~> 0.0.0, >= 0.0.2)
32
+ dry-validation (0.11.1)
33
+ concurrent-ruby (~> 1.0)
34
+ dry-configurable (~> 0.1, >= 0.1.3)
35
+ dry-core (~> 0.2, >= 0.2.1)
36
+ dry-equalizer (~> 0.2)
37
+ dry-logic (~> 0.4, >= 0.4.0)
38
+ dry-types (~> 0.12.0)
39
+ ice_nine (0.11.2)
40
+ inflecto (0.0.2)
41
+ mail (2.7.0)
42
+ mini_mime (>= 0.1.1)
43
+ method_source (0.9.0)
44
+ mini_mime (1.0.0)
45
+ pry (0.11.3)
46
+ coderay (~> 1.1.0)
47
+ method_source (~> 0.9.0)
48
+ rspec (3.7.0)
49
+ rspec-core (~> 3.7.0)
50
+ rspec-expectations (~> 3.7.0)
51
+ rspec-mocks (~> 3.7.0)
52
+ rspec-core (3.7.1)
53
+ rspec-support (~> 3.7.0)
54
+ rspec-expectations (3.7.0)
55
+ diff-lcs (>= 1.2.0, < 2.0)
56
+ rspec-support (~> 3.7.0)
57
+ rspec-mocks (3.7.0)
58
+ diff-lcs (>= 1.2.0, < 2.0)
59
+ rspec-support (~> 3.7.0)
60
+ rspec-support (3.7.0)
61
+
62
+ PLATFORMS
63
+ ruby
64
+
65
+ DEPENDENCIES
66
+ bundler
67
+ dry-struct
68
+ dry-types
69
+ dry-validation
70
+ mail
71
+ pry
72
+ rspec
73
+
74
+ BUNDLED WITH
75
+ 1.16.1
data/bin/console ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift 'lib'
4
+
5
+ require 'sipo_mailer'
6
+ require 'pry'
7
+
8
+ Pry.start
data/bin/mailer ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift 'lib'
4
+
5
+ require 'pry'
6
+
7
+ require 'sipo_mailer'
8
+
9
+ SipoMailer.perform
@@ -0,0 +1,4 @@
1
+ address_book: /sipo/spec/fixtures/adresar.csv
2
+ email:
3
+ address: @gmail.com
4
+ password:
@@ -0,0 +1,19 @@
1
+ version: "2"
2
+
3
+ volumes:
4
+ gem_store_240:
5
+ external: true
6
+
7
+ services:
8
+ web:
9
+ #image: web:ruby
10
+ build:
11
+ context: .
12
+ command: bash
13
+ env_file: .env
14
+ tty: true
15
+ stdin_open: true
16
+ volumes:
17
+ - gem_store_240:/gems
18
+ - /Users/landovsky/git/sipo:/sipo
19
+ - /Users/landovsky/git/sipo/docs:/docs
@@ -0,0 +1,51 @@
1
+ require 'yaml'
2
+ require 'sipo_mailer/utils/hash_extensions'
3
+
4
+ module SipoMailer
5
+ class Config
6
+ class ConfigMissing < StandardError; end
7
+
8
+ using HashExtensions
9
+
10
+ attr_accessor :yaml
11
+
12
+ def initialize
13
+ Installer.install unless File.exist? Installer.config
14
+ load_yaml
15
+ check_config
16
+ mailer_init
17
+ end
18
+
19
+ private
20
+
21
+ def check_config
22
+ raise ConfigMissing, 'address_book' unless yaml[:address_book]
23
+ raise ConfigMissing, 'email: address' unless yaml.dig(:email, :address)
24
+ raise ConfigMissing, 'email: password' unless yaml.dig(:email, :password)
25
+ rescue ConfigMissing => e
26
+ puts "V konfiguraci není vyplěna hodnota '#{e.message}'. Umístění konfigurace: #{SipoMailer::Installer.config}"
27
+ exit
28
+ end
29
+
30
+ def load_yaml
31
+ self.yaml = YAML.load_file(Installer.config).symbolize_keys
32
+ end
33
+
34
+ def mailer_init
35
+ options = email_options
36
+ Mail.defaults { delivery_method :smtp, options }
37
+ end
38
+
39
+ def email_options
40
+ {
41
+ address: 'smtp.gmail.com',
42
+ port: 587,
43
+ domain: 'gmail.com',
44
+ user_name: 'givit.cz@gmail.com',
45
+ password: yaml.dig(:email, :password),
46
+ authentication: :login,
47
+ enable_starttls_auto: true
48
+ }
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,73 @@
1
+ module SipoMailer
2
+ class Mailer
3
+ class DeliveryError < StandardError
4
+ end
5
+
6
+ class Response
7
+ attr_reader :response
8
+ def initialize(response = nil)
9
+ @response = response
10
+ end
11
+
12
+ def sent?
13
+ if @response.is_a? Mail::Message
14
+ @response&.errors.empty?
15
+ else
16
+ false
17
+ end
18
+ end
19
+ end
20
+
21
+ def initialize(to:, from:, subject:, attachment: nil)
22
+ @to = to
23
+ @from = from
24
+ @subject = subject
25
+ @attachment = attachment
26
+ end
27
+
28
+ def self.send(to: 'landovsky@gmail.com',
29
+ from: SipoMailer.config.yaml.dig(:email, :address),
30
+ subject: 'ahoj',
31
+ attachment: nil)
32
+ email = new(to: to, from: from, subject: subject, attachment: attachment)
33
+ begin
34
+ response = email.send
35
+ Response.new(response)
36
+ rescue DeliveryError => e
37
+ p e
38
+ end
39
+ end
40
+
41
+ def send
42
+ email.deliver!
43
+ rescue SocketError
44
+ print "\n\nChyba při posílání emailu: adresa " \
45
+ "#{Mail.delivery_method.settings[:address]} není dostupná.\n"
46
+ exit
47
+ rescue Net::SMTPAuthenticationError
48
+ print "\n\nChyba při posílání emailu: " \
49
+ "chybné přihlašovací údaje pro emailovou schránku.\n"
50
+ exit
51
+ rescue Net::OpenTimeout
52
+ print "\n\nChyba při posílání emailu: vypršel časový limit "\
53
+ "při spojení s emailovým serverem.\n"
54
+ exit
55
+ end
56
+
57
+ def email
58
+ email = Mail.new
59
+ email.to = @to
60
+ email.from = @from
61
+ email.subject = @subject
62
+ email.body = email_body
63
+ email.add_file(@attachment.path) if @attachment
64
+ email
65
+ end
66
+
67
+ private
68
+
69
+ def email_body
70
+ 'something simple'
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,90 @@
1
+ # Description
2
+ module SipoMailer
3
+ module Models
4
+ class AddressBook
5
+ attr_reader :contacts, :book
6
+
7
+ def initialize
8
+ @file = load_book
9
+ @contacts = without_dups
10
+ end
11
+
12
+ def self.find(id)
13
+ new.find(id)
14
+ end
15
+
16
+ def find(id)
17
+ find_all(id).first
18
+ end
19
+
20
+ private
21
+
22
+ def find_all(id)
23
+ @contacts.select { |c| c.id == id.to_s }
24
+ end
25
+
26
+ def without_dups
27
+ contacts = read_book
28
+ ids = contacts.map(&:id)
29
+ dups = ids.select { |id| ids.count(id) > 1 }.uniq
30
+ return contacts if dups.empty?
31
+
32
+ print "Duplicitní kontakty nebudou zpracovány:\n"
33
+ dups.each do |dup_id|
34
+ contacts.each do |contact|
35
+ printf(" %s: %s\n", contact.id, contact.email) if contact.id == dup_id
36
+ end
37
+ end
38
+
39
+ contacts.reject { |contact| dups.include?(contact.id) }
40
+ end
41
+
42
+ def load_book
43
+ file = SipoMailer.config.yaml[:address_book]
44
+ if file.nil?
45
+ print "Do nastavení dejte cestu k CSV souboru adresáře.\n"
46
+ exit
47
+ end
48
+ open(file)
49
+ rescue Errno::ENOENT
50
+ print "Soubor #{file} nelze otevřít. Máte správně nastavenou cestu?\n"
51
+ exit
52
+ end
53
+
54
+ def parse_contacts(header, book)
55
+ hashes = book.map { |contact| header.zip contact }.map(&:to_h)
56
+ hashes.map do |contact|
57
+ params = symbolize_keys(contact)
58
+ if Models::Contact.valid?(params)
59
+ Models::Contact.new(params)
60
+ else
61
+ row = contact.values.compact.join(', ')
62
+ print "Nepodařilo se zpracovat řádku adresáře: #{row}. "\
63
+ "Je správně oddělen středníkem?\n"
64
+ next
65
+ end
66
+ end.compact
67
+ end
68
+
69
+ def symbolize_keys(hsh)
70
+ {}.tap do |new_hash|
71
+ hsh.each_pair do |key, value|
72
+ new_hash[key.to_sym] = value
73
+ end
74
+ end
75
+ end
76
+
77
+ def read_book
78
+ book = []
79
+ File.open(@file, 'r') do |f|
80
+ f.each_line do |line|
81
+ book << line.delete("\n").split(';')
82
+ end
83
+ end
84
+ header = book[0]
85
+ book = book[1..-1]
86
+ parse_contacts(header, book)
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,36 @@
1
+ module SipoMailer
2
+ module Models
3
+ class Attachment
4
+ FILENAME_REGEX = /^[0-9]+_?([0-9]{4}-[0-9]{2}-[0-9]{2})?\.[a-zA-Z0-9]+$/
5
+
6
+ attr_reader :path, :id, :file, :filename, :processed_on
7
+
8
+ def initialize(filename)
9
+ @path = filename
10
+ @file = open(filename)
11
+ parse_meta
12
+ end
13
+
14
+ def valid?
15
+ !(@filename =~ FILENAME_REGEX).nil?
16
+ end
17
+
18
+ def processed?
19
+ !@processed_on.nil?
20
+ end
21
+
22
+ def process; end
23
+
24
+ private
25
+
26
+ def parse_meta
27
+ @filename = @file.path.split('/').last
28
+ name = @filename.split('.').first
29
+ try_date = name.split('_')
30
+
31
+ @id = try_date.first
32
+ @processed_on = try_date.last if try_date.size == 2
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,25 @@
1
+ require 'sipo_mailer/models/types'
2
+
3
+ module SipoMailer
4
+ module Models
5
+ class Contact < Dry::Struct
6
+ constructor_type :strict
7
+
8
+ attribute :cislo, Types::String
9
+ attribute :email, Types::String
10
+
11
+ def id
12
+ cislo
13
+ end
14
+
15
+ def self.valid?(params)
16
+ schema = Dry::Validation.Schema do
17
+ required(:cislo).filled
18
+ required(:email).filled
19
+ end
20
+
21
+ schema.call(params).errors.empty?
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,7 @@
1
+ module SipoMailer
2
+ module Models
3
+ module Types
4
+ include Dry::Types.module
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,39 @@
1
+ module HashExtensions
2
+ refine Hash do
3
+ def transform_keys
4
+ result = {}
5
+ each_key do |key|
6
+ result[yield(key)] = self[key]
7
+ end
8
+ result
9
+ end
10
+
11
+ def symbolize_keys
12
+ symbolized = {}
13
+ each do |key, value|
14
+ new_key = key.to_sym rescue key
15
+ new_val = if value.is_a? Hash
16
+ value.symbolize_keys
17
+ else
18
+ value
19
+ end
20
+ symbolized[new_key] = new_val
21
+ end
22
+ symbolized
23
+ end
24
+
25
+ def stringify_values
26
+ transformed = dup
27
+ transformed.each do |key, value|
28
+ new_val = if value.is_a? Hash
29
+ value
30
+ elsif value.is_a? Array
31
+ value
32
+ else
33
+ value.to_s
34
+ end
35
+ transformed[key] = new_val
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,56 @@
1
+ module SipoMailer
2
+ module Installer
3
+ DEFAULT_DIR = 'sipo_mailer'.freeze
4
+ CONFIG_FILE = 'config.yml'
5
+
6
+ class << self
7
+ def install
8
+ create_dir ? copy_config : nil
9
+ end
10
+
11
+ def create_dir
12
+ result = Dir.mkdir(config_dir) unless Dir.exist?(config_dir)
13
+ result == 0
14
+ end
15
+
16
+ def copy_config
17
+ return false if File.exist? config
18
+ File.open("#{config_dir}/#{CONFIG_FILE}", 'w') do |file|
19
+ example = File.open("config/#{CONFIG_FILE}.example")
20
+ file.write(example.readlines.join(''))
21
+ example.close
22
+ end
23
+ true
24
+ end
25
+
26
+ def config
27
+ [config_dir, CONFIG_FILE].join('/')
28
+ end
29
+
30
+ def config_dir
31
+ case os
32
+ when :windows
33
+ [ENV['HOME'], DEFAULT_DIR].join('/')
34
+ else
35
+ [ENV['USERPROFILE'], DEFAULT_DIR].join('/')
36
+ end
37
+ end
38
+
39
+ def os
40
+ host_os = RbConfig::CONFIG['host_os']
41
+ case host_os
42
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
43
+ :windows
44
+ when /darwin|mac os/
45
+ :macosx
46
+ when /linux/
47
+ :linux
48
+ when /solaris|bsd/
49
+ :unix
50
+ else
51
+ raise Error::WebDriverError, "unknown os: #{host_os.inspect}"
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,22 @@
1
+ # Test
2
+ module SipoMailer
3
+ class Testik
4
+ class Con
5
+ def initialize
6
+ init
7
+ @con = 1
8
+ end
9
+
10
+ def init
11
+ end
12
+ end
13
+
14
+ @config ||= Con.new
15
+
16
+ class << self
17
+ def con
18
+ @config
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ module SipoMailer
2
+ class Timer
3
+ def self.elapsed(&block)
4
+ start = Time.now
5
+ yield(block)
6
+ finish = Time.now
7
+ e = (finish - start) * 1000
8
+ p "Elapsed time: #{e} ms"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ## TODOs
4
+ # zpracovane prejmenovat
5
+
6
+ require 'pry'
7
+ require 'mail'
8
+ require 'dry-types'
9
+ require 'dry-struct'
10
+ require 'dry-validation'
11
+
12
+ $LOAD_PATH.unshift __dir__
13
+ Dir["#{__dir__}/**/*.rb"].each { |file| require file }
14
+
15
+ module SipoMailer
16
+ class << self
17
+ attr_accessor :config, :address_book
18
+ end
19
+
20
+ @config ||= Config.new
21
+ @address_book ||= Models::AddressBook.new
22
+
23
+ def self.perform
24
+ files = Dir[Dir.pwd + '/*.*']
25
+ files = files.map { |path| Models::Attachment.new(path) }.select(&:valid?)
26
+ files = files.reject { |file| file.processed? }
27
+
28
+ if files.none?
29
+ printf "Žádné soubory ke zpracování\n"
30
+ exit
31
+ end
32
+
33
+ printf "Našel jsem %d souborů ke zpracování.\n\n", files.count
34
+
35
+ files = files.reject do |file|
36
+ if SipoMailer.address_book.find(file.id).nil?
37
+ printf "Vyřazuji soubor #{file.filename}, číslo #{file.id} nenalezeno v adresáři.\n\n"
38
+ true
39
+ else
40
+ false
41
+ end
42
+ end
43
+
44
+ response = nil
45
+ until %w(a n).include?(response)
46
+ printf "Poslat %s soubor/ů?\n", files.count
47
+ print "(a) ano\n"
48
+ print "(n) ne\n"
49
+
50
+ response = STDIN.gets.strip
51
+ exit if response == 'n'
52
+ system('clear')
53
+ end
54
+
55
+ files.each do |file|
56
+ to = SipoMailer.address_book.find(file.id).email
57
+ printf 'Posílám soubor %s na %s', file.filename, to
58
+
59
+ email = Mailer.send(to: to, attachment: file)
60
+ printf " ok\n\n" if email.sent?
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,23 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'sipo_mailer'
3
+ s.version = '0.0.1'
4
+ s.summary = 'Command-line aplikace na odesílání souborů jako příloh na email dle CSV adresáře.'
5
+ s.author = 'Tomáš Landovský'
6
+ s.email = 'landovsky@gmail.com'
7
+ s.files = `git ls-files -z`.split("\x0").reject do |f|
8
+ f.match(%r{^(tmp|test|spec|features)/})
9
+ end
10
+ s.homepage = 'http://rubygems.org/gems/sipo_mailer'
11
+ s.license = 'MIT'
12
+ s.bindir = 'bin'
13
+ s.executables = 'mailer'
14
+
15
+ s.add_development_dependency 'bundler'
16
+ s.add_development_dependency 'pry'
17
+ s.add_development_dependency 'rspec'
18
+
19
+ s.add_runtime_dependency 'dry-struct'
20
+ s.add_runtime_dependency 'dry-types'
21
+ s.add_runtime_dependency 'dry-validation'
22
+ s.add_runtime_dependency 'mail'
23
+ end
metadata ADDED
@@ -0,0 +1,164 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sipo_mailer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Tomáš Landovský
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-02-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: dry-struct
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: dry-types
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: dry-validation
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: mail
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description:
112
+ email: landovsky@gmail.com
113
+ executables:
114
+ - mailer
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".gitignore"
119
+ - ".rubocop.yml"
120
+ - ".rubocop_todo.yml"
121
+ - Dockerfile
122
+ - Gemfile
123
+ - Gemfile.lock
124
+ - bin/console
125
+ - bin/mailer
126
+ - config/config.yml.example
127
+ - docker-compose.yml
128
+ - lib/sipo_mailer.rb
129
+ - lib/sipo_mailer/config.rb
130
+ - lib/sipo_mailer/email.rb
131
+ - lib/sipo_mailer/models/address_book.rb
132
+ - lib/sipo_mailer/models/attachment.rb
133
+ - lib/sipo_mailer/models/contact.rb
134
+ - lib/sipo_mailer/models/types.rb
135
+ - lib/sipo_mailer/utils/hash_extensions.rb
136
+ - lib/sipo_mailer/utils/installer.rb
137
+ - lib/sipo_mailer/utils/testik.rb
138
+ - lib/sipo_mailer/utils/timer.rb
139
+ - sipo_mailer.gemspec
140
+ homepage: http://rubygems.org/gems/sipo_mailer
141
+ licenses:
142
+ - MIT
143
+ metadata: {}
144
+ post_install_message:
145
+ rdoc_options: []
146
+ require_paths:
147
+ - lib
148
+ required_ruby_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ required_rubygems_version: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ requirements: []
159
+ rubyforge_project:
160
+ rubygems_version: 2.6.8
161
+ signing_key:
162
+ specification_version: 4
163
+ summary: Command-line aplikace na odesílání souborů jako příloh na email dle CSV adresáře.
164
+ test_files: []