yiffspace 0.0.1

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.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +0 -0
  3. data/LICENSE +20 -0
  4. data/README.md +25 -0
  5. data/Rakefile +10 -0
  6. data/app/assets/config/yiff_space_manifest.js +1 -0
  7. data/app/assets/stylesheets/yiff_space/application.css +30 -0
  8. data/app/controllers/yiff_space/application_controller.rb +6 -0
  9. data/app/helpers/yiff_space/application_helper.rb +6 -0
  10. data/app/helpers/yiff_space/auth_helper.rb +6 -0
  11. data/app/models/yiff_space/application_record.rb +7 -0
  12. data/app/views/layouts/yiff_space/application.html.erb +17 -0
  13. data/app/views/yiff_space/error.html.erb +3 -0
  14. data/engines/auth/app/controllers/yiff_space/auth/application_controller.rb +11 -0
  15. data/engines/auth/app/controllers/yiff_space/auth/root_controller.rb +67 -0
  16. data/engines/auth/app/views/yiff_space/auth/root/permissions.html.erb +14 -0
  17. data/engines/auth/config/routes.rb +11 -0
  18. data/lib/tasks/yiff_space_tasks.rake +6 -0
  19. data/lib/yiffspace/auth/auth_info/anonymous.rb +64 -0
  20. data/lib/yiffspace/auth/auth_info.rb +74 -0
  21. data/lib/yiffspace/auth/client.rb +58 -0
  22. data/lib/yiffspace/auth/discord_info.rb +63 -0
  23. data/lib/yiffspace/auth/engine.rb +57 -0
  24. data/lib/yiffspace/auth/helper.rb +167 -0
  25. data/lib/yiffspace/auth/permissions.rb +85 -0
  26. data/lib/yiffspace/auth/set_client_name.rb +15 -0
  27. data/lib/yiffspace/auth/user_info/anonymous.rb +72 -0
  28. data/lib/yiffspace/auth/user_info.rb +91 -0
  29. data/lib/yiffspace/auth.rb +84 -0
  30. data/lib/yiffspace/config_builder.rb +189 -0
  31. data/lib/yiffspace/configuration/images.rb +22 -0
  32. data/lib/yiffspace/configuration.rb +30 -0
  33. data/lib/yiffspace/core_ext/action_dispatch/set_auth_client/scoped.rb +13 -0
  34. data/lib/yiffspace/core_ext/action_dispatch/set_auth_client.rb +13 -0
  35. data/lib/yiffspace/core_ext/active_record/all.rb +3 -0
  36. data/lib/yiffspace/core_ext/active_record/where_chain.rb +6 -0
  37. data/lib/yiffspace/core_ext/all.rb +8 -0
  38. data/lib/yiffspace/core_ext/arel/all.rb +4 -0
  39. data/lib/yiffspace/core_ext/arel/cross_join_lateral.rb +21 -0
  40. data/lib/yiffspace/core_ext/arel/left_join_lateral.rb +21 -0
  41. data/lib/yiffspace/core_ext/enumerable/all.rb +3 -0
  42. data/lib/yiffspace/core_ext/enumerable/parallel.rb +7 -0
  43. data/lib/yiffspace/core_ext/hash/all.rb +3 -0
  44. data/lib/yiffspace/core_ext/hash/to_open_hash.rb +7 -0
  45. data/lib/yiffspace/core_ext/open_hash.rb +3 -0
  46. data/lib/yiffspace/core_ext/string/all.rb +5 -0
  47. data/lib/yiffspace/core_ext/string/sql.rb +7 -0
  48. data/lib/yiffspace/core_ext/string/to_b.rb +7 -0
  49. data/lib/yiffspace/core_ext/string/truthy_falsy.rb +7 -0
  50. data/lib/yiffspace/extensions/action_dispatch/set_auth_client/scoped.rb +15 -0
  51. data/lib/yiffspace/extensions/action_dispatch/set_auth_client.rb +13 -0
  52. data/lib/yiffspace/extensions/active_record/where_chain.rb +107 -0
  53. data/lib/yiffspace/extensions/arel/nodes/cross_join_lateral.rb +17 -0
  54. data/lib/yiffspace/extensions/arel/nodes/left_join_lateral.rb +17 -0
  55. data/lib/yiffspace/extensions/arel/table/cross_join_lateral.rb +15 -0
  56. data/lib/yiffspace/extensions/arel/table/left_join_lateral.rb +15 -0
  57. data/lib/yiffspace/extensions/arel/visitors/postgresql/cross_join_lateral.rb +19 -0
  58. data/lib/yiffspace/extensions/arel/visitors/postgresql/left_join_lateral.rb +23 -0
  59. data/lib/yiffspace/extensions/enumerable/parallel.rb +39 -0
  60. data/lib/yiffspace/extensions/hash/to_open_hash.rb +15 -0
  61. data/lib/yiffspace/extensions/string/sql.rb +21 -0
  62. data/lib/yiffspace/extensions/string/to_b.rb +13 -0
  63. data/lib/yiffspace/extensions/string/truthy_falsy.rb +17 -0
  64. data/lib/yiffspace/images/avatar/base.rb +26 -0
  65. data/lib/yiffspace/images/avatar/discord.rb +37 -0
  66. data/lib/yiffspace/images/avatar/gravatar.rb +21 -0
  67. data/lib/yiffspace/images/avatar.rb +29 -0
  68. data/lib/yiffspace/images/banner/base.rb +26 -0
  69. data/lib/yiffspace/images/banner/discord.rb +37 -0
  70. data/lib/yiffspace/images/banner.rb +29 -0
  71. data/lib/yiffspace/images.rb +6 -0
  72. data/lib/yiffspace/utils/open_hash.rb +54 -0
  73. data/lib/yiffspace/utils/set_env_constraint.rb +18 -0
  74. data/lib/yiffspace/utils.rb +6 -0
  75. data/lib/yiffspace/version.rb +5 -0
  76. data/lib/yiffspace.rb +11 -0
  77. metadata +176 -0
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YiffSpace
4
+ class Configuration
5
+ class Images
6
+ attr_accessor(:server_url, :update_token, :default_avatar_type, :default_banner_type, :httparty_options)
7
+
8
+ def initialize
9
+ @server_url = "https://images.yiff.space"
10
+ @default_avatar_type = :discord
11
+ @default_banner_type = :discord
12
+ @httparty_options = {
13
+ timeout: 10,
14
+ open_timeout: 5,
15
+ headers: {
16
+ "User-Agent" => "YiffSpaceRuby/#{YiffSpace::VERSION} (https://yiff.space)",
17
+ },
18
+ }
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YiffSpace
4
+ class Configuration
5
+ def auth(&block)
6
+ client = YiffSpace::Auth.register(Auth::DEFAULT_CLIENT_NAME) unless YiffSpace::Auth.instance_variable_get(:@clients).key?(Auth::DEFAULT_CLIENT_NAME)
7
+ client ||= YiffSpace::Auth[Auth::DEFAULT_CLIENT_NAME]
8
+ block&.call(client)
9
+ client
10
+ end
11
+
12
+ def add_auth(name, &)
13
+ YiffSpace::Auth.register(name, &)
14
+ end
15
+
16
+ def images
17
+ @images ||= Images.new
18
+ end
19
+ end
20
+
21
+ class << self
22
+ def config
23
+ @config ||= Configuration.new
24
+ end
25
+
26
+ def configure
27
+ yield(config)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative("../../../extensions/action_dispatch/set_auth_client/scoped")
4
+
5
+ module ActionDispatch
6
+ module Routing
7
+ class Mapper
8
+ module Scoping
9
+ include(YiffSpace::Extensions::ActionDispatch::SetAuthClient::Scoped)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative("../../extensions/action_dispatch/set_auth_client")
4
+
5
+ module ActionDispatch
6
+ module Routing
7
+ class Mapper
8
+ module Scoping
9
+ include(YiffSpace::Extensions::ActionDispatch::SetAuthClient)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative("where_chain")
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require("active_record/relation")
4
+ require_relative("../../extensions/active_record/where_chain")
5
+
6
+ ActiveRecord::QueryMethods::WhereChain.include(YiffSpace::Extensions::ActiveRecord::WhereChain)
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative("active_record/where_chain")
4
+ require_relative("arel/all")
5
+ require_relative("enumerable/all")
6
+ require_relative("hash/all")
7
+ require_relative("string/all")
8
+ require_relative("open_hash")
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative("cross_join_lateral")
4
+ require_relative("left_join_lateral")
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative("../../extensions/arel/nodes/cross_join_lateral")
4
+ require_relative("../../extensions/arel/visitors/postgresql/cross_join_lateral")
5
+ require_relative("../../extensions/arel/table/cross_join_lateral")
6
+
7
+ module Arel
8
+ module Nodes
9
+ CrossJoinLateral = YiffSpace::Extensions::Arel::Nodes::CrossJoinLateral
10
+ end
11
+
12
+ module Visitors
13
+ class PostgreSQL
14
+ include(YiffSpace::Extensions::Arel::Visitors::PostgreSQL::CrossJoinLateral)
15
+ end
16
+ end
17
+
18
+ class Table
19
+ include(YiffSpace::Extensions::Arel::Table::CrossJoinLateral)
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative("../../extensions/arel/nodes/left_join_lateral")
4
+ require_relative("../../extensions/arel/visitors/postgresql/left_join_lateral")
5
+ require_relative("../../extensions/arel/table/left_join_lateral")
6
+
7
+ module Arel
8
+ module Nodes
9
+ LeftJoinLateral = YiffSpace::Extensions::Arel::Nodes::LeftJoinLateral
10
+ end
11
+
12
+ module Visitors
13
+ class PostgreSQL
14
+ include(YiffSpace::Extensions::Arel::Visitors::PostgreSQL::LeftJoinLateral)
15
+ end
16
+ end
17
+
18
+ class Table
19
+ include(YiffSpace::Extensions::Arel::Table::LeftJoinLateral)
20
+ end
21
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative("parallel")
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative("../../extensions/enumerable/parallel")
4
+
5
+ module Enumerable
6
+ include(YiffSpace::Extensions::Enumerable::Parallel)
7
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative("to_open_hash")
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative("../../extensions/hash/to_open_hash")
4
+
5
+ class Hash
6
+ include(YiffSpace::Extensions::Hash::ToOpenHash)
7
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ OpenHash = YiffSpace::Utils::OpenHash
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative("sql")
4
+ require_relative("to_b")
5
+ require_relative("truthy_falsy")
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative("../../extensions/string/sql")
4
+
5
+ class String
6
+ include(YiffSpace::Extensions::String::Sql)
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative("../../extensions/string/to_b")
4
+
5
+ class String
6
+ include(YiffSpace::Extensions::String::ToB)
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative("../../extensions/string/truthy_falsy")
4
+
5
+ class String
6
+ include(YiffSpace::Extensions::String::TruthyFalsy)
7
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YiffSpace
4
+ module Extensions
5
+ module ActionDispatch
6
+ module SetAuthClient
7
+ module Scoped
8
+ def yiffspace_set_auth_client(name, &)
9
+ constraints(Auth::SetClientName.new(name), &)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YiffSpace
4
+ module Extensions
5
+ module ActionDispatch
6
+ module SetAuthClient
7
+ def set_auth_client(name, &)
8
+ constraints(Auth::SetClientName.new(name), &)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YiffSpace
4
+ module Extensions
5
+ module ActiveRecord
6
+ module WhereChain
7
+ %i[
8
+ lt lteq gt gteq
9
+ between not_between in not_in like
10
+ ilike not_like not_ilike
11
+ like_all ilike_all not_like_all not_ilike_all
12
+ like_any ilike_any not_like_any not_ilike_any
13
+ has_bits not_has_bits
14
+ ].each do |method|
15
+ define_method(method) do |conditions|
16
+ build(conditions, method)
17
+ end
18
+ end
19
+
20
+ alias lte lteq
21
+ alias gte gteq
22
+
23
+ # https://www.postgresql.org/docs/current/static/functions-matching.html#FUNCTIONS-POSIX-REGEXP
24
+ # "(?e)" means force use of ERE syntax; see sections 9.7.3.1 and 9.7.3.4.
25
+ def regex(conditions = {}, flags: "e", **kwargs)
26
+ conditions.merge!(kwargs) if conditions.is_a?(Hash)
27
+ build(conditions, :regex, flags)
28
+ end
29
+
30
+ def not_regex(conditions = {}, flags: "e", **kwargs)
31
+ conditions.merge!(kwargs) if conditions.is_a?(Hash)
32
+ build(conditions, :not_regex, flags)
33
+ end
34
+
35
+ def tsquery(conditions = {}, ts_config: "english", **kwargs)
36
+ conditions.merge!(kwargs) if conditions.is_a?(Hash)
37
+ build(conditions, :tsquery, ts_config)
38
+ end
39
+
40
+ METHODS = {
41
+ lt: ->(table_name, column, value) { Arel::Table.new(table_name)[column].lt(value) },
42
+ lteq: ->(table_name, column, value) { Arel::Table.new(table_name)[column].lteq(value) },
43
+ gt: ->(table_name, column, value) { Arel::Table.new(table_name)[column].gt(value) },
44
+ gteq: ->(table_name, column, value) { Arel::Table.new(table_name)[column].gteq(value) },
45
+ between: ->(table_name, column, value) { Arel::Table.new(table_name)[column].between(value) },
46
+ not_between: ->(table_name, column, value) { Arel::Table.new(table_name)[column].not_between(value) },
47
+ in: ->(table_name, column, value) { Arel::Table.new(table_name)[column].in(value) },
48
+ not_in: ->(table_name, column, value) { Arel::Table.new(table_name)[column].not_in(value) },
49
+ like: ->(table_name, column, value) { Arel::Table.new(table_name)[column].matches(value.to_escaped_for_sql_like, Arel.sql("E'\\\\'"), true) },
50
+ ilike: ->(table_name, column, value) { Arel::Table.new(table_name)[column].matches(value.to_escaped_for_sql_like, Arel.sql("E'\\\\'"), false) },
51
+ not_like: ->(table_name, column, value) { Arel::Table.new(table_name)[column].does_not_match(value.to_escaped_for_sql_like, Arel.sql("E'\\\\'"), true) },
52
+ not_ilike: ->(table_name, column, value) { Arel::Table.new(table_name)[column].does_not_match(value.to_escaped_for_sql_like, Arel.sql("E'\\\\'"), false) },
53
+ like_all: ->(table_name, column, value) { Arel::Table.new(table_name)[column].matches_all(value.map(&:to_escaped_for_sql_like), Arel.sql("E'\\\\'"), true) },
54
+ ilike_all: ->(table_name, column, value) { Arel::Table.new(table_name)[column].matches_all(value.map(&:to_escaped_for_sql_like), Arel.sql("E'\\\\'"), false) },
55
+ not_like_all: ->(table_name, column, value) { Arel::Table.new(table_name)[column].does_not_match_all(value.map(&:to_escaped_for_sql_like), Arel.sql("E'\\\\'"), true) },
56
+ not_ilike_all: ->(table_name, column, value) { Arel::Table.new(table_name)[column].does_not_match_all(value.map(&:to_escaped_for_sql_like), Arel.sql("E'\\\\'"), false) },
57
+ like_any: ->(table_name, column, value) { Arel::Table.new(table_name)[column].matches_any(value.map(&:to_escaped_for_sql_like), Arel.sql("E'\\\\'"), true) },
58
+ ilike_any: ->(table_name, column, value) { Arel::Table.new(table_name)[column].matches_any(value.map(&:to_escaped_for_sql_like), Arel.sql("E'\\\\'"), false) },
59
+ not_like_any: ->(table_name, column, value) { Arel::Table.new(table_name)[column].does_not_match_any(value.map(&:to_escaped_for_sql_like), Arel.sql("E'\\\\'"), true) },
60
+ not_ilike_any: ->(table_name, column, value) { Arel::Table.new(table_name)[column].does_not_match_any(value.map(&:to_escaped_for_sql_like), Arel.sql("E'\\\\'"), false) },
61
+ regex: ->(table_name, column, value, flags) { Arel::Table.new(table_name)[column].matches_regexp("(?#{flags})#{value.is_a?(Regexp) ? value.source : value}") },
62
+ not_regex: ->(table_name, column, value, flags) { Arel::Table.new(table_name)[column].does_not_match_regexp("(?#{flags})#{value.is_a?(Regexp) ? value.source : value}") },
63
+ tsquery: ->(table_name, column, value, ts_config) { Arel.sql("to_tsvector(:ts_config, :table) @@ plainto_tsquery(:ts_config, :value)", ts_config: ts_config, value: value, table: Arel.sql("#{table_name}.#{column}")) },
64
+ has_bits: ->(table_name, column, value) { Arel.sql("(#{table_name}.#{column} & #{Arel::Nodes.build_quoted(value).to_sql}) = #{Arel::Nodes.build_quoted(value).to_sql}") },
65
+ not_has_bits: ->(table_name, column, value) { Arel.sql("(#{table_name}.#{column} & #{Arel::Nodes.build_quoted(value).to_sql}) != #{Arel::Nodes.build_quoted(value).to_sql}") },
66
+ }.freeze
67
+
68
+ private
69
+
70
+ def build(conditions, method, *extra)
71
+ model = @scope.klass
72
+
73
+ parsed_conditions = normalize_conditions(conditions, model)
74
+
75
+ arel_conditions = parsed_conditions.map do |(table_name, column), value|
76
+ METHODS.fetch(method).call(table_name, column, value, *extra)
77
+ end.reduce(:and)
78
+
79
+ @scope.where(arel_conditions)
80
+ end
81
+
82
+ def normalize_conditions(conditions, model, table_name = nil)
83
+ pairs = []
84
+
85
+ conditions.each do |key, value|
86
+ case key
87
+ when String, Symbol
88
+ key = key.to_s
89
+ if value.is_a?(Hash)
90
+ pairs.concat(normalize_conditions(value, model, key))
91
+ elsif key.include?(".")
92
+ table, col = key.split(".", 2)
93
+ pairs << [[table, col], value]
94
+ else
95
+ pairs << [[table_name || model.table_name, key], value]
96
+ end
97
+ else
98
+ raise(ArgumentError, "Unsupported key type: #{key.class}")
99
+ end
100
+ end
101
+
102
+ pairs
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require("arel")
4
+
5
+ module YiffSpace
6
+ module Extensions
7
+ module Arel
8
+ module Nodes
9
+ class CrossJoinLateral < ::Arel::Nodes::Join
10
+ def initialize(left, right = nil)
11
+ super
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require("arel")
4
+
5
+ module YiffSpace
6
+ module Extensions
7
+ module Arel
8
+ module Nodes
9
+ class LeftJoinLateral < ::Arel::Nodes::Join
10
+ def initialize(left, right = nil)
11
+ super
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YiffSpace
4
+ module Extensions
5
+ module Arel
6
+ module Table
7
+ module CrossJoinLateral
8
+ def cross_join_lateral(relation)
9
+ join(relation, ::Arel::Nodes::CrossJoinLateral)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YiffSpace
4
+ module Extensions
5
+ module Arel
6
+ module Table
7
+ module LeftJoinLateral
8
+ def left_join_lateral(relation)
9
+ join(relation, ::Arel::Nodes::LeftJoinLateral)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YiffSpace
4
+ module Extensions
5
+ module Arel
6
+ module Visitors
7
+ module PostgreSQL
8
+ module CrossJoinLateral
9
+ # noinspection RubyInstanceMethodNamingConvention
10
+ def visit_Arel_Nodes_CrossJoinLateral(o, collector)
11
+ collector << "CROSS JOIN LATERAL "
12
+ visit(o.left, collector)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YiffSpace
4
+ module Extensions
5
+ module Arel
6
+ module Visitors
7
+ module PostgreSQL
8
+ module LeftJoinLateral
9
+ # noinspection RubyInstanceMethodNamingConvention
10
+ def visit_Arel_Nodes_LeftJoinLateral(o, collector)
11
+ collector << "LEFT JOIN LATERAL "
12
+ visit(o.left, collector)
13
+ return unless o.right
14
+
15
+ collector << " ON "
16
+ visit(o.right.expr, collector)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require("active_support/core_ext/module/attribute_accessors")
4
+
5
+ module YiffSpace
6
+ module Extensions
7
+ module Enumerable
8
+ module Parallel
9
+ mattr_accessor(:usable)
10
+ # Like `#each`, but perform the block on each item in parallel. Note that items aren't processed in order, so things
11
+ # like `parallel_each.map` that rely on ordering won't work.
12
+ def parallel_each(executor = :io, &)
13
+ return enum_for(:parallel_each, executor) unless block_given?
14
+
15
+ parallel_map(executor, &)
16
+ self
17
+ end
18
+
19
+ # Like `#map`, but in parallel.
20
+ def parallel_map(executor = :io, &)
21
+ return enum_for(:parallel_map, executor) unless block_given?
22
+
23
+ promises = map do |item|
24
+ Concurrent::Promises.future_on(executor, item, &)
25
+ end
26
+
27
+ Concurrent::Promises.zip_futures_on(executor, *promises).value!
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ begin
35
+ require("concurrent-ruby")
36
+ YiffSpace::Extensions::Enumerable::Parallel.usable = true
37
+ rescue LoadError
38
+ YiffSpace::Extensions::Enumerable::Parallel.usable = false
39
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YiffSpace
4
+ module Extensions
5
+ module Hash
6
+ module ToOpenHash
7
+ def to_open_hash
8
+ ::YiffSpace::Utils::OpenHash.from(self)
9
+ end
10
+
11
+ alias with_open_access to_open_hash
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YiffSpace
4
+ module Extensions
5
+ module String
6
+ module Sql
7
+ def to_escaped_for_sql_like
8
+ gsub(/%|_|\*|\\\*|\\\\|\\/) do |str|
9
+ case str
10
+ when "%" then '\%'
11
+ when "_" then '\_'
12
+ when "*" then "%"
13
+ when '\*' then "*"
14
+ when "\\\\", "\\" then "\\\\"
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YiffSpace
4
+ module Extensions
5
+ module String
6
+ module ToB
7
+ def to_b # rubocop:disable Naming/PredicateMethod
8
+ !match?(/\A(false|f|no|n|off|0)\z/i)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YiffSpace
4
+ module Extensions
5
+ module String
6
+ module TruthyFalsy
7
+ def truthy?
8
+ match?(/\A(true|t|yes|y|on|1)\z/i)
9
+ end
10
+
11
+ def falsy?
12
+ match?(/\A(false|f|no|n|off|0)\z/i)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YiffSpace
4
+ module Images
5
+ module Avatar
6
+ class Base
7
+ def self.type = nil
8
+
9
+ attr_reader(:id, :type)
10
+
11
+ def initialize(id, type)
12
+ @id = id
13
+ @type = type
14
+ end
15
+
16
+ def url
17
+ raise(NotImplementedError)
18
+ end
19
+
20
+ def update(*)
21
+ raise(NotImplementedError)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require("httparty")
4
+
5
+ module YiffSpace
6
+ module Images
7
+ module Avatar
8
+ class Discord < Base
9
+ def self.type = :discord
10
+
11
+ def initialize(id)
12
+ super(id, self.class.type)
13
+ end
14
+
15
+ def url
16
+ "#{YiffSpace.config.images.server_url}/avatar/discord/#{id}"
17
+ end
18
+
19
+ def update(hash)
20
+ raise(StandardError, "images.update_token config option must be set to use avatar update") if YiffSpace.config.images.update_token.blank?
21
+
22
+ response = HTTParty.post("#{YiffSpace.config.images.server_url}/avatar/discord/update/#{id}",
23
+ YiffSpace.config.images.httparty_options.deep_merge(
24
+ headers: {
25
+ "Authorization" => "Bearer #{YiffSpace.config.images.update_token}",
26
+ "Content-Type" => "application/json",
27
+ },
28
+ body: { hash: hash }.to_json,
29
+ ))
30
+ Rails.logger.error("avatar update failed: #{response.to_json}") unless response.key?("updated")
31
+
32
+ response.fetch("updated", false)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end