yiffspace 0.1.8 → 0.2.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 +4 -4
- data/CHANGELOG.md +4 -10
- data/README.md +18 -1
- data/Rakefile +6 -7
- data/lib/yiffspace/version.rb +1 -1
- data/lib/yiffspace.rb +26 -28
- metadata +104 -148
- data/app/assets/config/yiffspace_manifest.js +0 -1
- data/app/assets/stylesheets/yiffspace/application.css +0 -30
- data/app/controllers/yiff_space/application_controller.rb +0 -6
- data/app/helpers/yiff_space/application_helper.rb +0 -6
- data/app/models/yiff_space/application_record.rb +0 -7
- data/app/views/layouts/yiff_space/application.html.erb +0 -17
- data/app/views/yiff_space/error.html.erb +0 -3
- data/lib/generators/yiffspace/fixer/USAGE +0 -40
- data/lib/generators/yiffspace/fixer/fixer_generator.rb +0 -86
- data/lib/generators/yiffspace/fixer/templates/fixer.rb +0 -4
- data/lib/generators/yiffspace/install/fixes_generator.rb +0 -21
- data/lib/generators/yiffspace/install/templates/create_fixes.rb.erb +0 -18
- data/lib/tasks/fixes.rake +0 -66
- data/lib/yiffspace/concerns/active_record_extensions.rb +0 -45
- data/lib/yiffspace/concerns/api_methods.rb +0 -101
- data/lib/yiffspace/concerns/attribute_matchers.rb +0 -100
- data/lib/yiffspace/concerns/attribute_methods.rb +0 -39
- data/lib/yiffspace/concerns/concurrency_methods.rb +0 -20
- data/lib/yiffspace/concerns/conditional_includes.rb +0 -43
- data/lib/yiffspace/concerns/current_methods.rb +0 -60
- data/lib/yiffspace/concerns/has_bit_flags.rb +0 -59
- data/lib/yiffspace/concerns/user_class_methods.rb +0 -32
- data/lib/yiffspace/concerns/user_methods.rb +0 -77
- data/lib/yiffspace/concerns/user_name_methods.rb +0 -53
- data/lib/yiffspace/concerns/user_resolvable_methods.rb +0 -38
- data/lib/yiffspace/config_builder.rb +0 -192
- data/lib/yiffspace/configuration/fixer_templates.rb +0 -87
- data/lib/yiffspace/configuration/images.rb +0 -22
- data/lib/yiffspace/configuration.rb +0 -130
- data/lib/yiffspace/core_ext/active_record/all.rb +0 -8
- data/lib/yiffspace/core_ext/active_record/cross_join_lateral.rb +0 -7
- data/lib/yiffspace/core_ext/active_record/cross_unnest.rb +0 -7
- data/lib/yiffspace/core_ext/active_record/left_join_lateral.rb +0 -7
- data/lib/yiffspace/core_ext/active_record/left_unnest.rb +0 -7
- data/lib/yiffspace/core_ext/active_record/unnest.rb +0 -8
- data/lib/yiffspace/core_ext/active_record/where_chain.rb +0 -6
- data/lib/yiffspace/core_ext/all.rb +0 -8
- data/lib/yiffspace/core_ext/arel/all.rb +0 -4
- data/lib/yiffspace/core_ext/arel/cross_join_lateral.rb +0 -21
- data/lib/yiffspace/core_ext/arel/left_join_lateral.rb +0 -21
- data/lib/yiffspace/core_ext/enumerable/all.rb +0 -3
- data/lib/yiffspace/core_ext/enumerable/parallel.rb +0 -7
- data/lib/yiffspace/core_ext/hash/all.rb +0 -3
- data/lib/yiffspace/core_ext/hash/to_open_hash.rb +0 -7
- data/lib/yiffspace/core_ext/object/all.rb +0 -4
- data/lib/yiffspace/core_ext/object/to_b.rb +0 -7
- data/lib/yiffspace/core_ext/object/truthy_falsy.rb +0 -7
- data/lib/yiffspace/core_ext/string/all.rb +0 -3
- data/lib/yiffspace/core_ext/string/sql.rb +0 -7
- data/lib/yiffspace/engine.rb +0 -23
- data/lib/yiffspace/extensions/active_record/cross_join_lateral.rb +0 -23
- data/lib/yiffspace/extensions/active_record/cross_unnest.rb +0 -15
- data/lib/yiffspace/extensions/active_record/left_join_lateral.rb +0 -24
- data/lib/yiffspace/extensions/active_record/left_unnest.rb +0 -15
- data/lib/yiffspace/extensions/active_record/unnest.rb +0 -20
- data/lib/yiffspace/extensions/active_record/where_chain.rb +0 -107
- data/lib/yiffspace/extensions/arel/nodes/cross_join_lateral.rb +0 -17
- data/lib/yiffspace/extensions/arel/nodes/left_join_lateral.rb +0 -17
- data/lib/yiffspace/extensions/arel/table/cross_join_lateral.rb +0 -15
- data/lib/yiffspace/extensions/arel/table/left_join_lateral.rb +0 -15
- data/lib/yiffspace/extensions/arel/visitors/postgresql/cross_join_lateral.rb +0 -19
- data/lib/yiffspace/extensions/arel/visitors/postgresql/left_join_lateral.rb +0 -23
- data/lib/yiffspace/extensions/enumerable/parallel.rb +0 -39
- data/lib/yiffspace/extensions/hash/to_open_hash.rb +0 -15
- data/lib/yiffspace/extensions/object/to_b.rb +0 -22
- data/lib/yiffspace/extensions/object/truthy_falsy.rb +0 -35
- data/lib/yiffspace/extensions/string/sql.rb +0 -21
- data/lib/yiffspace/fix_tracker.rb +0 -95
- data/lib/yiffspace/fixer_template.rb +0 -44
- data/lib/yiffspace/images/avatar/base.rb +0 -41
- data/lib/yiffspace/images/avatar/discord.rb +0 -37
- data/lib/yiffspace/images/avatar/gravatar.rb +0 -21
- data/lib/yiffspace/images/avatar.rb +0 -33
- data/lib/yiffspace/images/banner/base.rb +0 -41
- data/lib/yiffspace/images/banner/discord.rb +0 -37
- data/lib/yiffspace/images/banner.rb +0 -33
- data/lib/yiffspace/images.rb +0 -6
- data/lib/yiffspace/include/active_record_extensions.rb +0 -5
- data/lib/yiffspace/include/all.rb +0 -31
- data/lib/yiffspace/include/api_methods.rb +0 -5
- data/lib/yiffspace/include/attribute_matchers.rb +0 -5
- data/lib/yiffspace/include/attribute_methods.rb +0 -5
- data/lib/yiffspace/include/cache.rb +0 -5
- data/lib/yiffspace/include/concurrency_methods.rb +0 -5
- data/lib/yiffspace/include/conditional_includes.rb +0 -5
- data/lib/yiffspace/include/current.rb +0 -5
- data/lib/yiffspace/include/current_methods.rb +0 -5
- data/lib/yiffspace/include/duration_parser.rb +0 -5
- data/lib/yiffspace/include/has_bit_flags.rb +0 -5
- data/lib/yiffspace/include/helpers.rb +0 -5
- data/lib/yiffspace/include/open_hash.rb +0 -5
- data/lib/yiffspace/include/parameter_builder.rb +0 -5
- data/lib/yiffspace/include/parse_value.rb +0 -5
- data/lib/yiffspace/include/query_builder.rb +0 -5
- data/lib/yiffspace/include/query_dsl.rb +0 -5
- data/lib/yiffspace/include/query_helper.rb +0 -5
- data/lib/yiffspace/include/routes.rb +0 -5
- data/lib/yiffspace/include/table_builder.rb +0 -5
- data/lib/yiffspace/include/trace_logger.rb +0 -5
- data/lib/yiffspace/include/u2id.rb +0 -5
- data/lib/yiffspace/include/user_attribute.rb +0 -5
- data/lib/yiffspace/include/user_class_methods.rb +0 -5
- data/lib/yiffspace/include/user_like.rb +0 -5
- data/lib/yiffspace/include/user_methods.rb +0 -5
- data/lib/yiffspace/include/user_name_methods.rb +0 -5
- data/lib/yiffspace/include/user_resolvable.rb +0 -5
- data/lib/yiffspace/include/user_resolvable_methods.rb +0 -5
- data/lib/yiffspace/search/query_builder.rb +0 -83
- data/lib/yiffspace/search/query_dsl.rb +0 -119
- data/lib/yiffspace/search/query_helper.rb +0 -49
- data/lib/yiffspace/serializers/avatar_serializer.rb +0 -21
- data/lib/yiffspace/serializers/banner_serializer.rb +0 -21
- data/lib/yiffspace/serializers/user_resolvable_serializer.rb +0 -21
- data/lib/yiffspace/utils/cache.rb +0 -40
- data/lib/yiffspace/utils/current.rb +0 -43
- data/lib/yiffspace/utils/duration_parser.rb +0 -24
- data/lib/yiffspace/utils/helpers.rb +0 -25
- data/lib/yiffspace/utils/open_hash.rb +0 -63
- data/lib/yiffspace/utils/parameter_builder.rb +0 -121
- data/lib/yiffspace/utils/parse_value.rb +0 -174
- data/lib/yiffspace/utils/routes.rb +0 -28
- data/lib/yiffspace/utils/set_env_constraint.rb +0 -18
- data/lib/yiffspace/utils/table_builder.rb +0 -169
- data/lib/yiffspace/utils/trace_logger.rb +0 -91
- data/lib/yiffspace/utils/user_attribute.rb +0 -272
- data/lib/yiffspace/utils/user_like.rb +0 -8
- data/lib/yiffspace/utils/user_resolvable.rb +0 -61
- data/lib/yiffspace/utils/user_to_id.rb +0 -11
- data/lib/yiffspace/utils.rb +0 -6
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Yiffspace
|
|
4
|
-
# Scaffolds a new db/fixes/*.rb script for YiffSpace::FixTracker (see `rake fixes:list`).
|
|
5
|
-
#
|
|
6
|
-
# `--steps N` splits the fix into N ordered steps sharing an id, each one a copy of the generic
|
|
7
|
-
# fixer.rb template. A host app can instead define a YiffSpace::FixerTemplate subclass (see that
|
|
8
|
-
# class) to control both the step count and each step's file content, auto-discovered from
|
|
9
|
-
# YiffSpace.config.fixer_templates_path - reachable here with `--template <name>`/`-t <name>`,
|
|
10
|
-
# or with the template's own shortname flag if it registered one (e.g. `-e`).
|
|
11
|
-
class FixerGenerator < Rails::Generators::NamedBase
|
|
12
|
-
source_root(File.expand_path("templates", __dir__))
|
|
13
|
-
# No short alias - -s/-f/-p/-q are already claimed by Rails::Generators::Base's own runtime
|
|
14
|
-
# options (--skip/--force/--pretend/--quiet), and Thor silently lets the last-defined
|
|
15
|
-
# class_option win a collision instead of erroring.
|
|
16
|
-
class_option(:steps, type: :numeric, default: 1,
|
|
17
|
-
desc: "Split the fix into N ordered steps sharing an id, e.g. 12_1_name.rb, 12_2_name.rb")
|
|
18
|
-
class_option(:template, type: :string, default: nil, aliases: ["-t"],
|
|
19
|
-
desc: "Use a fixer template registered with YiffSpace.config.fixer_templates")
|
|
20
|
-
|
|
21
|
-
YiffSpace.config.fixer_templates.each do |template|
|
|
22
|
-
next unless template.short
|
|
23
|
-
|
|
24
|
-
class_option(template.name.to_sym, type: :boolean, default: false, aliases: ["-#{template.short}"],
|
|
25
|
-
desc: "Shortcut for --template #{template.name}")
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def create_fixer
|
|
29
|
-
content_blocks = resolve_content_blocks
|
|
30
|
-
|
|
31
|
-
id = Dir[File.join(destination_root, "db/fixes/*.rb")].map { |f| File.basename(f, ".rb").split("_").first.to_i }.max.to_i + 1
|
|
32
|
-
|
|
33
|
-
if content_blocks.size == 1
|
|
34
|
-
create_fixer_file("db/fixes/#{id}_#{file_name}.rb", content_blocks.first)
|
|
35
|
-
else
|
|
36
|
-
content_blocks.each_with_index { |block, index| create_fixer_file("db/fixes/#{id}_#{index + 1}_#{file_name}.rb", block) }
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
private
|
|
41
|
-
|
|
42
|
-
# One entry per step - a block returning that step's file content, or nil to fall back to
|
|
43
|
-
# copying the generic fixer.rb template (a plain --steps N, or a manually
|
|
44
|
-
# YiffSpace.config.fixer_templates.register'd template, has no per-step content).
|
|
45
|
-
def resolve_content_blocks
|
|
46
|
-
template = resolve_template
|
|
47
|
-
return template.content_blocks if template
|
|
48
|
-
|
|
49
|
-
Array.new(steps_from_flag)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def resolve_template
|
|
53
|
-
name = template_name
|
|
54
|
-
return nil if name.nil?
|
|
55
|
-
|
|
56
|
-
template = YiffSpace.config.fixer_templates[name]
|
|
57
|
-
raise(Thor::Error, "no fixer template named #{name.inspect} is registered") unless template
|
|
58
|
-
|
|
59
|
-
template
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# The template chosen via `--template <name>`, or via a registered template's own shortname
|
|
63
|
-
# flag (e.g. `-e` for a template registered with `short: "e"`) - nil if neither was passed.
|
|
64
|
-
def template_name
|
|
65
|
-
return options["template"] if options["template"]
|
|
66
|
-
|
|
67
|
-
YiffSpace.config.fixer_templates.find { |template| template.short && options[template.name] }&.name
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def steps_from_flag
|
|
71
|
-
steps = options["steps"].to_i
|
|
72
|
-
raise(Thor::Error, "--steps must be at least 1") if steps < 1
|
|
73
|
-
|
|
74
|
-
steps
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def create_fixer_file(path, content_block)
|
|
78
|
-
if content_block
|
|
79
|
-
create_file(path, content_block.call)
|
|
80
|
-
chmod(path, "+x")
|
|
81
|
-
else
|
|
82
|
-
copy_file("fixer.rb", path, mode: :preserve)
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
end
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Yiffspace
|
|
4
|
-
module Install
|
|
5
|
-
# Installs YiffSpace::FixTracker into a host app: `bin/rails generate yiffspace:install:fixes`
|
|
6
|
-
# copies the `fixes` tracking table migration. Run `bin/rails db:migrate` afterwards.
|
|
7
|
-
class FixesGenerator < Rails::Generators::Base
|
|
8
|
-
include(Rails::Generators::Migration)
|
|
9
|
-
|
|
10
|
-
source_root(File.expand_path("templates", __dir__))
|
|
11
|
-
|
|
12
|
-
def self.next_migration_number(dirname)
|
|
13
|
-
ActiveRecord::Migration.next_migration_number(current_migration_number(dirname) + 1)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def create_migration_file
|
|
17
|
-
migration_template("create_fixes.rb.erb", "db/migrate/create_fixes.rb")
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Tracks which db/fixes/*.rb scripts have been applied, the same way schema_migrations tracks
|
|
4
|
-
# migrations - see YiffSpace::FixTracker and `rake fixes:migrate`/`rake fixes:list`.
|
|
5
|
-
#
|
|
6
|
-
# `id` is the fix's own leading number (e.g. 12, 23), not a surrogate key, so it isn't unique on
|
|
7
|
-
# its own - a fix can be split into ordered sub-steps (12_1, 12_2, ...) that share an id and are
|
|
8
|
-
# distinguished by `index`. `index` is null for fixes with no subtype.
|
|
9
|
-
class CreateFixes < ActiveRecord::Migration[<%= ActiveRecord::VERSION::STRING.to_f %>]
|
|
10
|
-
def change
|
|
11
|
-
create_table(:fixes, id: false) do |t|
|
|
12
|
-
t.integer(:id, null: false) # not a surrogate key, this is the fix's own leading number
|
|
13
|
-
t.integer(:index)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
add_index(:fixes, %i[id index], unique: true, nulls_not_distinct: true)
|
|
17
|
-
end
|
|
18
|
-
end
|
data/lib/tasks/fixes.rake
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Keep structure.sql's fixes rows in sync with the database on every schema dump, the same way
|
|
4
|
-
# ActiveRecord keeps schema_migrations rows in sync - not just when `fixes:migrate` runs.
|
|
5
|
-
Rake::Task["db:schema:dump"].enhance do
|
|
6
|
-
YiffSpace::FixTracker.dump_structure_sql!
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
namespace(:fixes) do
|
|
10
|
-
desc("List available fix scripts in db/fixes, marking which have already been applied")
|
|
11
|
-
task(list: :environment) do
|
|
12
|
-
applied = YiffSpace::FixTracker.applied
|
|
13
|
-
YiffSpace::FixTracker.all_fixes.each do |name|
|
|
14
|
-
puts("#{applied.include?(YiffSpace::FixTracker.key_for(name)) ? '[x]' : '[ ]'} #{name}")
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
desc("Run a fix script from db/fixes by id or name, e.g. `rake fixes:run[1]` - re-runs even if already recorded as applied")
|
|
19
|
-
task(:run, [:name] => :environment) do |_task, args|
|
|
20
|
-
name = args[:name].to_s.delete_suffix(".rb")
|
|
21
|
-
abort("Usage: rake fixes:run[id_or_name] (see `rake fixes:list`)") if name.blank?
|
|
22
|
-
|
|
23
|
-
candidates = Dir["db/fixes/*.rb"]
|
|
24
|
-
exact = candidates.find { |path| File.basename(path, ".rb") == name }
|
|
25
|
-
matches = exact ? [exact] : candidates.select { |path| File.basename(path, ".rb").start_with?("#{name}_") || File.basename(path, ".rb").include?(name) }
|
|
26
|
-
|
|
27
|
-
if matches.empty?
|
|
28
|
-
abort("No fix matches \"#{name}\". Run `rake fixes:list` to see available fixes.")
|
|
29
|
-
elsif matches.size > 1
|
|
30
|
-
abort("Multiple fixes match \"#{name}\", be more specific:\n#{matches.map { |m| " #{File.basename(m, '.rb')}" }.join("\n")}")
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
puts("Running #{File.basename(matches.first, '.rb')}...")
|
|
34
|
-
name = YiffSpace::FixTracker.run!(matches.first)
|
|
35
|
-
puts("Recorded #{name} as applied")
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
desc("Apply all fix scripts not yet recorded as applied, in order, then dump the schema like db:migrate does")
|
|
39
|
-
task(migrate: :environment) do
|
|
40
|
-
pending = YiffSpace::FixTracker.pending
|
|
41
|
-
|
|
42
|
-
if pending.empty?
|
|
43
|
-
puts("No pending fixes.")
|
|
44
|
-
else
|
|
45
|
-
pending.each do |name|
|
|
46
|
-
puts("Running #{name}...")
|
|
47
|
-
YiffSpace::FixTracker.run!(YiffSpace::FixTracker.fix_path(name))
|
|
48
|
-
puts("Recorded #{name} as applied")
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
Rake::Task["db:_dump"].invoke
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
desc("Record all of the current fixes as having been applied")
|
|
56
|
-
task(load: :environment) do
|
|
57
|
-
list = YiffSpace::FixTracker.all_fixes.map { YiffSpace::FixTracker.key_for(it) }
|
|
58
|
-
existing = ActiveRecord::Base.connection.select_all('SELECT id, "index" FROM fixes ORDER BY id, "index"').rows
|
|
59
|
-
list.reject! { |id, index| existing.any? { |e| e[0] == id && e[1] == index } }
|
|
60
|
-
unless list.empty?
|
|
61
|
-
values = list.map { |id, index| "(#{id}, #{index.nil? ? 'NULL' : index})" }.join(", ")
|
|
62
|
-
ActiveRecord::Base.connection.execute(%(INSERT INTO fixes (id, "index") VALUES #{values}))
|
|
63
|
-
end
|
|
64
|
-
puts("Loaded #{list.length} #{'fix'.pluralize(list.length)}")
|
|
65
|
-
end
|
|
66
|
-
end
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module YiffSpace
|
|
4
|
-
module Concerns
|
|
5
|
-
module ActiveRecordExtensions
|
|
6
|
-
extend(ActiveSupport::Concern)
|
|
7
|
-
|
|
8
|
-
module ClassMethods
|
|
9
|
-
def without_timeout
|
|
10
|
-
original = statement_timeout
|
|
11
|
-
connection.execute("SET STATEMENT_TIMEOUT = 0") unless Rails.env.test?
|
|
12
|
-
yield
|
|
13
|
-
ensure
|
|
14
|
-
connection.execute("SET STATEMENT_TIMEOUT = #{original}") unless Rails.env.test?
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def with_timeout(time, default_value = nil)
|
|
18
|
-
original = statement_timeout
|
|
19
|
-
connection.execute("SET STATEMENT_TIMEOUT = #{time}") unless Rails.env.test?
|
|
20
|
-
yield
|
|
21
|
-
rescue ::ActiveRecord::StatementInvalid => e
|
|
22
|
-
Rails.logger.warn("Statement timeout exceeded: #{e.message}")
|
|
23
|
-
default_value
|
|
24
|
-
ensure
|
|
25
|
-
connection.execute("SET STATEMENT_TIMEOUT = #{original}") unless Rails.env.test?
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def statement_timeout
|
|
29
|
-
YiffSpace.config.application_record_class.connection.select_one("SELECT setting FROM pg_settings WHERE name = 'statement_timeout'")["setting"].to_i
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# CrossJoinLateral, LeftJoinLateral, nil
|
|
33
|
-
def unnest(column, name: column.singularize, type: ::Arel::Nodes::LeftJoinLateral)
|
|
34
|
-
function = ::Arel::Nodes::NamedFunction.new("unnest", [arel(column)], name)
|
|
35
|
-
return function if type.nil?
|
|
36
|
-
|
|
37
|
-
joins(type.new(
|
|
38
|
-
function,
|
|
39
|
-
::Arel::Nodes::On.new(Arel.sql("TRUE")),
|
|
40
|
-
))
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module YiffSpace
|
|
4
|
-
module Concerns
|
|
5
|
-
module ApiMethods
|
|
6
|
-
class NotVisibleError < StandardError; end
|
|
7
|
-
extend(ActiveSupport::Concern)
|
|
8
|
-
|
|
9
|
-
module ClassMethods
|
|
10
|
-
def available_includes
|
|
11
|
-
[]
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def multiple_includes
|
|
15
|
-
reflections.select { |_, v| v.macro == :has_many }.keys.map(&:to_sym)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def associated_models(name)
|
|
19
|
-
if reflections[name].options[:polymorphic]
|
|
20
|
-
reflections[name].active_record.try(:model_types) || []
|
|
21
|
-
else
|
|
22
|
-
[reflections[name].class_name]
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
delegate(:available_includes, to: :class)
|
|
28
|
-
|
|
29
|
-
# XXX deprecated, shouldn't expose this as an instance method.
|
|
30
|
-
def api_attributes(user)
|
|
31
|
-
policy = Pundit.policy(user, self) || ApplicationPolicy.new(user, self)
|
|
32
|
-
policy.api_attributes
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
# XXX deprecated, shouldn't expose this as an instance method.
|
|
36
|
-
def html_data_attributes(user)
|
|
37
|
-
policy = Pundit.policy(user, self) || ApplicationPolicy.new(user, self)
|
|
38
|
-
policy.html_data_attributes
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def process_api_attributes(options, underscore: false)
|
|
42
|
-
options[:methods] ||= []
|
|
43
|
-
attributes, methods = api_attributes(options[:user]).partition { |attr| has_attribute?(attr) }
|
|
44
|
-
methods += options[:methods]
|
|
45
|
-
if underscore && options[:only].blank?
|
|
46
|
-
options[:only] = attributes + methods
|
|
47
|
-
else
|
|
48
|
-
options[:only] ||= attributes + methods
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
attributes &= options[:only]
|
|
52
|
-
methods &= options[:only]
|
|
53
|
-
|
|
54
|
-
options[:only] = attributes
|
|
55
|
-
options[:methods] = methods
|
|
56
|
-
|
|
57
|
-
options.delete(:methods) if options[:methods].empty?
|
|
58
|
-
options
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def serializable_hash(options = {})
|
|
62
|
-
options ||= {}
|
|
63
|
-
options[:user] ||= CurrentUser.user || User.anonymous
|
|
64
|
-
return :not_visible unless visible?(options[:user])
|
|
65
|
-
|
|
66
|
-
if options[:only].is_a?(String)
|
|
67
|
-
options.delete(:methods)
|
|
68
|
-
options.delete(:include)
|
|
69
|
-
options.merge!(ParameterBuilder.serial_parameters(options[:only], self, options))
|
|
70
|
-
if options[:only].include?("_")
|
|
71
|
-
options[:only].delete("_")
|
|
72
|
-
options = process_api_attributes(options, underscore: true)
|
|
73
|
-
end
|
|
74
|
-
else
|
|
75
|
-
options = process_api_attributes(options)
|
|
76
|
-
end
|
|
77
|
-
options[:only] += [SecureRandom.hex(6)]
|
|
78
|
-
|
|
79
|
-
hash = super
|
|
80
|
-
hash.transform_keys! { |key| key.delete("?").delete_prefix("apionly_") }
|
|
81
|
-
deep_reject_hash(hash) { |_, v| v == :not_visible }
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def visible?(_user)
|
|
85
|
-
true
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def deep_reject_hash(hash, &block)
|
|
89
|
-
hash.each_with_object({}) do |(key, value), result|
|
|
90
|
-
if value.is_a?(Hash)
|
|
91
|
-
result[key] = deep_reject_hash(value, &block)
|
|
92
|
-
elsif value.is_a?(Array)
|
|
93
|
-
result[key] = value.map { |v| v.is_a?(Hash) ? deep_reject_hash(v, &block) : v }.reject { |i| block.call(nil, i) }
|
|
94
|
-
elsif !block.call(key, value)
|
|
95
|
-
result[key] = value
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
end
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module YiffSpace
|
|
4
|
-
module Concerns
|
|
5
|
-
module AttributeMatchers
|
|
6
|
-
def boolean_attribute_matches(attribute, value, &)
|
|
7
|
-
return all if value.nil?
|
|
8
|
-
|
|
9
|
-
if value.to_s.truthy?
|
|
10
|
-
value = true
|
|
11
|
-
elsif value.to_s.falsy?
|
|
12
|
-
value = false
|
|
13
|
-
else
|
|
14
|
-
raise(ArgumentError, "value must be truthy or falsy")
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
q = block_given? ? yield : all
|
|
18
|
-
q.where(attribute => value)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# range: "5", ">5", "<5", ">=5", "<=5", "5..10", "5,6,7"
|
|
22
|
-
def numeric_attribute_matches(attribute, value, &)
|
|
23
|
-
return all if value.nil?
|
|
24
|
-
|
|
25
|
-
value = value.to_s.strip
|
|
26
|
-
column = QueryHelper.get_column(attribute, table_name)
|
|
27
|
-
parsed_range = ParseValue.range(value, column.type)
|
|
28
|
-
|
|
29
|
-
add_range_relation(parsed_range, attribute, &)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# datetime columns return ActiveSupport::TimeWithZone
|
|
33
|
-
def datetime_attribute_matches(attribute, range, &)
|
|
34
|
-
numeric_attribute_matches(attribute, range, &)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def add_range_relation(arr, field, &)
|
|
38
|
-
return all if arr.nil?
|
|
39
|
-
|
|
40
|
-
q = block_given? ? yield : all
|
|
41
|
-
case arr[0]
|
|
42
|
-
when :eq
|
|
43
|
-
if arr[1].is_a?(Time)
|
|
44
|
-
q.where.between(field => arr[1].all_day)
|
|
45
|
-
else
|
|
46
|
-
q.where(field => arr[1])
|
|
47
|
-
end
|
|
48
|
-
when :gt
|
|
49
|
-
q.where.gt(field => arr[1])
|
|
50
|
-
when :gte
|
|
51
|
-
q.where.gte(field => arr[1])
|
|
52
|
-
when :lt
|
|
53
|
-
q.where.lt(field => arr[1])
|
|
54
|
-
when :lte
|
|
55
|
-
q.where.lte(field => arr[1])
|
|
56
|
-
when :in
|
|
57
|
-
q.where.in(field => arr[1])
|
|
58
|
-
when :between
|
|
59
|
-
q.where.between(field => arr[1]..arr[2])
|
|
60
|
-
else
|
|
61
|
-
q.none
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def text_attribute_matches(attribute, value, convert_to_wildcard: false, &)
|
|
66
|
-
return all if value.nil?
|
|
67
|
-
|
|
68
|
-
value = "*#{value}*" if convert_to_wildcard && value.exclude?("*")
|
|
69
|
-
q = block_given? ? yield : all
|
|
70
|
-
if value.is_a?(Array)
|
|
71
|
-
q.where.ilike_any(attribute => value)
|
|
72
|
-
elsif value =~ /\*/
|
|
73
|
-
q.where.ilike(attribute => value)
|
|
74
|
-
else
|
|
75
|
-
q.where.tsquery(attribute => value)
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def ip_attribute_matches(attribute, value, &)
|
|
80
|
-
return all if value.nil?
|
|
81
|
-
|
|
82
|
-
q = block_given? ? yield : all
|
|
83
|
-
q.where.inet_contained_within_or_equals(attribute => value)
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def attribute_present(attribute, value, &)
|
|
87
|
-
return all if value.nil?
|
|
88
|
-
|
|
89
|
-
q = block_given? ? yield : all
|
|
90
|
-
if value.to_s.truthy?
|
|
91
|
-
q.where.not(attribute => nil)
|
|
92
|
-
elsif value.to_s.falsy?
|
|
93
|
-
q.where(attribute => nil)
|
|
94
|
-
else
|
|
95
|
-
q.none
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
end
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module YiffSpace
|
|
4
|
-
module Concerns
|
|
5
|
-
module AttributeMethods
|
|
6
|
-
extend(ActiveSupport::Concern)
|
|
7
|
-
|
|
8
|
-
module ClassMethods
|
|
9
|
-
# Defines `<attribute>_string`, `<attribute>_string=`, and `<attribute>=`
|
|
10
|
-
# methods for converting an array attribute to or from a string.
|
|
11
|
-
#
|
|
12
|
-
# The `<attribute>=` setter parses strings into an array using the
|
|
13
|
-
# `parse` regex. The resulting strings can be converted to another type
|
|
14
|
-
# with the `cast` option.
|
|
15
|
-
def array_attribute(name, parse: /[^[:space:]]+/, join_character: " ", cast: :itself)
|
|
16
|
-
define_method("#{name}_string") do
|
|
17
|
-
send(name).join(join_character)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
define_method("#{name}_string=") do |value|
|
|
21
|
-
raise(ArgumentError, "#{name} must be a String") unless value.respond_to?(:to_str)
|
|
22
|
-
|
|
23
|
-
send("#{name}=", value)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
define_method("#{name}=") do |value|
|
|
27
|
-
if value.respond_to?(:to_str)
|
|
28
|
-
super(value.to_str.scan(parse).flatten.map(&cast))
|
|
29
|
-
elsif value.respond_to?(:to_a)
|
|
30
|
-
super(value.to_a)
|
|
31
|
-
else
|
|
32
|
-
raise(ArgumentError, "#{name} must be a String or an Array")
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module YiffSpace
|
|
4
|
-
module Concerns
|
|
5
|
-
module ConcurrencyMethods
|
|
6
|
-
extend(ActiveSupport::Concern)
|
|
7
|
-
|
|
8
|
-
class_methods do
|
|
9
|
-
def parallel_find_each(**, &)
|
|
10
|
-
# XXX We may deadlock if a transaction is open; do a non-parallel each.
|
|
11
|
-
return find_each(&) if connection.transaction_open?
|
|
12
|
-
|
|
13
|
-
find_in_batches(error_on_ignore: true, **) do |batch|
|
|
14
|
-
batch.parallel_each(&)
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module YiffSpace
|
|
4
|
-
module Concerns
|
|
5
|
-
module ConditionalIncludes
|
|
6
|
-
extend(ActiveSupport::Concern)
|
|
7
|
-
|
|
8
|
-
module ClassMethods
|
|
9
|
-
def html_includes(request, *)
|
|
10
|
-
includes_if(request.format.html?, *)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def includes_if(condition, *)
|
|
14
|
-
return all unless condition
|
|
15
|
-
|
|
16
|
-
includes(*)
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def includes_unless(condition, *)
|
|
20
|
-
return all if condition
|
|
21
|
-
|
|
22
|
-
includes(*)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def html_preload(request, *)
|
|
26
|
-
preload_if(request.format.html?, *)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def preload_if(condition, *)
|
|
30
|
-
return all unless condition
|
|
31
|
-
|
|
32
|
-
preload(*)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def preload_unless(condition, *)
|
|
36
|
-
return all if condition
|
|
37
|
-
|
|
38
|
-
preload(*)
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module YiffSpace
|
|
4
|
-
module Concerns
|
|
5
|
-
module CurrentMethods
|
|
6
|
-
extend(ActiveSupport::Concern)
|
|
7
|
-
|
|
8
|
-
module ClassMethods
|
|
9
|
-
def search_current(params, ...)
|
|
10
|
-
search(params, YiffSpace.config.current_class.user, ...)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def with_current(instance, method, *args, **, &)
|
|
14
|
-
attr = args.grep(Symbol)
|
|
15
|
-
other = args - attr
|
|
16
|
-
hashes = other.grep(Hash)
|
|
17
|
-
params = other.grep(ActionController::Parameters)
|
|
18
|
-
other -= hashes + params
|
|
19
|
-
other.compact_blank!
|
|
20
|
-
options = [attr.index_with(YiffSpace.config.current_class.user), *hashes, *params.map(&:to_hash)].inject(:merge)
|
|
21
|
-
instance.send(method, *other, **options, **, &)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def new_with_current(...)
|
|
25
|
-
with_current(self, :new, ...)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def create_with_current(...)
|
|
29
|
-
with_current(self, :create, ...)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def create_with_current!(...)
|
|
33
|
-
with_current(self, :create!, ...)
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def update_with_current(...)
|
|
38
|
-
self.class.with_current(self, :update, ...)
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def update_with_current!(...)
|
|
42
|
-
self.class.with_current(self, :update!, ...)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def destroy_with_current(*attrs)
|
|
46
|
-
attrs.each do |attr|
|
|
47
|
-
send("#{attr}=", YiffSpace.config.current_class.user)
|
|
48
|
-
end
|
|
49
|
-
destroy
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def destroy_with_current!(*attrs)
|
|
53
|
-
attrs.each do |attr|
|
|
54
|
-
send("#{attr}=", YiffSpace.config.current_class.user)
|
|
55
|
-
end
|
|
56
|
-
destroy!
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module YiffSpace
|
|
4
|
-
module Concerns
|
|
5
|
-
module HasBitFlags
|
|
6
|
-
extend(ActiveSupport::Concern)
|
|
7
|
-
|
|
8
|
-
module ClassMethods
|
|
9
|
-
# NOTE: the ordering of attributes has to be fixed
|
|
10
|
-
# new attributes should be appended to the end.
|
|
11
|
-
def has_bit_flags(attributes, options = {})
|
|
12
|
-
field = options[:field] || :bit_flags
|
|
13
|
-
|
|
14
|
-
define_singleton_method("flag_value_for") do |key|
|
|
15
|
-
value = attributes[key.to_s]
|
|
16
|
-
return value if value
|
|
17
|
-
|
|
18
|
-
raise(ArgumentError, "Invalid flag: #{key}")
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
attributes.each do |name, value|
|
|
22
|
-
scope(name.to_sym, -> { where.has_bits(field => value) })
|
|
23
|
-
define_method(name) do
|
|
24
|
-
send(field) & value == value
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
define_method("#{name}=") do |val|
|
|
28
|
-
if val.to_s =~ /[t1y]/
|
|
29
|
-
send("#{field}=", send(field) | value)
|
|
30
|
-
else
|
|
31
|
-
send("#{field}=", send(field) & ~value)
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
define_method("#{name}_was") do
|
|
36
|
-
send("#{field}_was") & value == value
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
define_method("#{name}_before_last_save") do
|
|
40
|
-
send("#{field}_before_last_save") & value == value
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
alias_method("#{name}?", name)
|
|
44
|
-
alias_method("#{name}_before_last_save?", "#{name}_before_last_save")
|
|
45
|
-
alias_method("#{name}_was?", "#{name}_was")
|
|
46
|
-
|
|
47
|
-
define_method("#{name}_changed?") do
|
|
48
|
-
send("#{name}_was") != send(name)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
define_method("saved_change_to_#{name}?") do
|
|
52
|
-
send("#{name}_before_last_save") != send(name)
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|