rhino_project_core 0.20.0.alpha.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +28 -0
- data/Rakefile +35 -0
- data/app/assets/stripe_flow.png +0 -0
- data/app/controllers/concerns/rhino/authenticated.rb +18 -0
- data/app/controllers/concerns/rhino/error_handling.rb +60 -0
- data/app/controllers/concerns/rhino/paper_trail_whodunnit.rb +11 -0
- data/app/controllers/concerns/rhino/permit.rb +38 -0
- data/app/controllers/concerns/rhino/set_current_user.rb +13 -0
- data/app/controllers/rhino/account_controller.rb +34 -0
- data/app/controllers/rhino/active_model_extension_controller.rb +52 -0
- data/app/controllers/rhino/base_controller.rb +23 -0
- data/app/controllers/rhino/crud_controller.rb +57 -0
- data/app/controllers/rhino/simple_controller.rb +13 -0
- data/app/controllers/rhino/simple_stream_controller.rb +12 -0
- data/app/helpers/rhino/omniauth_helper.rb +67 -0
- data/app/helpers/rhino/policy_helper.rb +42 -0
- data/app/helpers/rhino/segment_helper.rb +62 -0
- data/app/models/rhino/account.rb +13 -0
- data/app/models/rhino/current.rb +7 -0
- data/app/models/rhino/user.rb +44 -0
- data/app/overrides/active_record/autosave_association_override.rb +18 -0
- data/app/overrides/active_record/delegated_type_override.rb +14 -0
- data/app/overrides/activestorage/direct_uploads_controller_override.rb +23 -0
- data/app/overrides/activestorage/redirect_controller_override.rb +21 -0
- data/app/overrides/activestorage/redirect_representation_controller_override.rb +21 -0
- data/app/overrides/devise_token_auth/confirmations_controller_override.rb +14 -0
- data/app/overrides/devise_token_auth/omniauth_callbacks_controller_override.rb +45 -0
- data/app/overrides/devise_token_auth/passwords_controller_override.rb +9 -0
- data/app/overrides/devise_token_auth/registrations_controller_override.rb +20 -0
- data/app/overrides/devise_token_auth/sessions_controller_override.rb +26 -0
- data/app/overrides/devise_token_auth/token_validations_controller_override.rb +18 -0
- data/app/policies/rhino/account_policy.rb +27 -0
- data/app/policies/rhino/active_storage_attachment_policy.rb +16 -0
- data/app/policies/rhino/admin_policy.rb +20 -0
- data/app/policies/rhino/base_policy.rb +72 -0
- data/app/policies/rhino/crud_policy.rb +109 -0
- data/app/policies/rhino/editor_policy.rb +12 -0
- data/app/policies/rhino/global_policy.rb +8 -0
- data/app/policies/rhino/resource_info_policy.rb +9 -0
- data/app/policies/rhino/user_policy.rb +20 -0
- data/app/policies/rhino/viewer_policy.rb +19 -0
- data/app/resources/rhino/info_graph.rb +41 -0
- data/app/resources/rhino/open_api_info.rb +108 -0
- data/config/routes.rb +19 -0
- data/db/migrate/20180101000000_devise_token_auth_create_users.rb +54 -0
- data/db/migrate/20180622142754_add_allow_change_password_to_users.rb +5 -0
- data/db/migrate/20191217010224_add_approved_to_users.rb +7 -0
- data/db/migrate/20200503182019_change_tokens_to_json_b.rb +9 -0
- data/lib/commands/rhino/module/coverage_command.rb +44 -0
- data/lib/commands/rhino/module/dummy_command.rb +43 -0
- data/lib/commands/rhino/module/new_command.rb +34 -0
- data/lib/commands/rhino/module/rails_command.rb +43 -0
- data/lib/commands/rhino/module/test_command.rb +43 -0
- data/lib/generators/rhino/dev/setup/setup_generator.rb +199 -0
- data/lib/generators/rhino/dev/setup/templates/env.client.tt +4 -0
- data/lib/generators/rhino/dev/setup/templates/env.root.tt +1 -0
- data/lib/generators/rhino/dev/setup/templates/env.server.tt +35 -0
- data/lib/generators/rhino/dev/setup/templates/prepare-commit-msg +17 -0
- data/lib/generators/rhino/install/install_generator.rb +24 -0
- data/lib/generators/rhino/install/templates/account.rb +4 -0
- data/lib/generators/rhino/install/templates/rhino.rb +24 -0
- data/lib/generators/rhino/install/templates/user.rb +4 -0
- data/lib/generators/rhino/model/model_generator.rb +96 -0
- data/lib/generators/rhino/module/USAGE +6 -0
- data/lib/generators/rhino/module/module_generator.rb +92 -0
- data/lib/generators/rhino/module/templates/%name%.gemspec.tt +24 -0
- data/lib/generators/rhino/module/templates/lib/%namespaced_name%/engine.rb.tt +18 -0
- data/lib/generators/rhino/module/templates/lib/generators/%namespaced_name%/install/install_generator.rb.tt +12 -0
- data/lib/generators/rhino/module/templates/lib/tasks/%namespaced_name%_tasks.rake.tt +13 -0
- data/lib/generators/rhino/module/templates/test/dummy/app/models/user.rb +4 -0
- data/lib/generators/rhino/module/templates/test/dummy/config/database.yml +25 -0
- data/lib/generators/rhino/module/templates/test/dummy/config/initializers/devise.rb +311 -0
- data/lib/generators/rhino/module/templates/test/dummy/config/initializers/devise_token_auth.rb +71 -0
- data/lib/generators/rhino/module/templates/test/test_helper.rb +54 -0
- data/lib/generators/rhino/policy/policy_generator.rb +33 -0
- data/lib/generators/rhino/policy/templates/policy.rb.tt +46 -0
- data/lib/generators/test_unit/rhino_policy_generator.rb +13 -0
- data/lib/generators/test_unit/templates/policy_test.rb.tt +57 -0
- data/lib/rhino/engine.rb +166 -0
- data/lib/rhino/omniauth/strategies/azure_o_auth2.rb +16 -0
- data/lib/rhino/resource/active_model_extension/backing_store/google_sheet.rb +89 -0
- data/lib/rhino/resource/active_model_extension/backing_store.rb +33 -0
- data/lib/rhino/resource/active_model_extension/describe.rb +38 -0
- data/lib/rhino/resource/active_model_extension/params.rb +70 -0
- data/lib/rhino/resource/active_model_extension/properties.rb +231 -0
- data/lib/rhino/resource/active_model_extension/reference.rb +50 -0
- data/lib/rhino/resource/active_model_extension/routing.rb +15 -0
- data/lib/rhino/resource/active_model_extension/serialization.rb +16 -0
- data/lib/rhino/resource/active_model_extension.rb +38 -0
- data/lib/rhino/resource/active_record_extension/describe.rb +44 -0
- data/lib/rhino/resource/active_record_extension/params.rb +213 -0
- data/lib/rhino/resource/active_record_extension/properties.rb +85 -0
- data/lib/rhino/resource/active_record_extension/properties_describe.rb +228 -0
- data/lib/rhino/resource/active_record_extension/reference.rb +50 -0
- data/lib/rhino/resource/active_record_extension/routing.rb +21 -0
- data/lib/rhino/resource/active_record_extension/search.rb +23 -0
- data/lib/rhino/resource/active_record_extension/serialization.rb +16 -0
- data/lib/rhino/resource/active_record_extension/super_admin.rb +25 -0
- data/lib/rhino/resource/active_record_extension.rb +32 -0
- data/lib/rhino/resource/active_record_tree.rb +50 -0
- data/lib/rhino/resource/active_storage_extension.rb +41 -0
- data/lib/rhino/resource/describe.rb +19 -0
- data/lib/rhino/resource/owner.rb +172 -0
- data/lib/rhino/resource/params.rb +31 -0
- data/lib/rhino/resource/properties.rb +192 -0
- data/lib/rhino/resource/reference.rb +29 -0
- data/lib/rhino/resource/routing.rb +107 -0
- data/lib/rhino/resource/serialization.rb +13 -0
- data/lib/rhino/resource/sieves.rb +36 -0
- data/lib/rhino/resource.rb +55 -0
- data/lib/rhino/sieve/filter.rb +149 -0
- data/lib/rhino/sieve/geospatial.rb +45 -0
- data/lib/rhino/sieve/helpers.rb +11 -0
- data/lib/rhino/sieve/limit.rb +20 -0
- data/lib/rhino/sieve/offset.rb +16 -0
- data/lib/rhino/sieve/order.rb +143 -0
- data/lib/rhino/sieve/search.rb +20 -0
- data/lib/rhino/sieve.rb +159 -0
- data/lib/rhino/test_case/controller.rb +145 -0
- data/lib/rhino/test_case/model.rb +86 -0
- data/lib/rhino/test_case/override.rb +19 -0
- data/lib/rhino/test_case/policy.rb +76 -0
- data/lib/rhino/test_case.rb +11 -0
- data/lib/rhino/version.rb +17 -0
- data/lib/rhino_project_core.rb +131 -0
- data/lib/tasks/rhino.rake +24 -0
- data/lib/tasks/rhino_dev.rake +17 -0
- data/lib/validators/country_validator.rb +11 -0
- data/lib/validators/email_validator.rb +8 -0
- data/lib/validators/ipv4_validator.rb +10 -0
- data/lib/validators/mac_address_validator.rb +9 -0
- metadata +531 -0
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "properties_describe"
|
4
|
+
|
5
|
+
module Rhino
|
6
|
+
module Resource
|
7
|
+
module ActiveRecordExtension
|
8
|
+
module Properties
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
include Rhino::Resource::ActiveRecordExtension::PropertiesDescribe
|
12
|
+
|
13
|
+
class_methods do # rubocop:disable Metrics/BlockLength
|
14
|
+
def identifier_property
|
15
|
+
primary_key
|
16
|
+
end
|
17
|
+
|
18
|
+
def readable_properties
|
19
|
+
props = attribute_names - foreign_key_properties
|
20
|
+
|
21
|
+
props.concat(reference_properties)
|
22
|
+
|
23
|
+
props.map(&:to_s)
|
24
|
+
end
|
25
|
+
|
26
|
+
def creatable_properties
|
27
|
+
writeable_properties
|
28
|
+
end
|
29
|
+
|
30
|
+
def updatable_properties
|
31
|
+
# https://api.rubyonrails.org/classes/ActiveRecord/ReadonlyAttributes/ClassMethods.html#method-i-readonly_attributes
|
32
|
+
writeable_properties - readonly_attributes.to_a
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
# FIXME: Duplicated in params.rb
|
37
|
+
def reference_to_sym(reference)
|
38
|
+
reference.is_a?(Hash) ? reference.keys.first : reference
|
39
|
+
end
|
40
|
+
|
41
|
+
# FIXME: Include counter caches as well
|
42
|
+
def automatic_properties
|
43
|
+
[identifier_property] + send(:all_timestamp_attributes_in_model)
|
44
|
+
end
|
45
|
+
|
46
|
+
def virtual_properties
|
47
|
+
columns.select(&:virtual?).map(&:name) # rubocop:todo Performance/SelectMap
|
48
|
+
end
|
49
|
+
|
50
|
+
def foreign_key_properties
|
51
|
+
reflect_on_all_associations(:belongs_to).map(&:foreign_key).map(&:to_s)
|
52
|
+
end
|
53
|
+
|
54
|
+
# rubocop:todo Style/OptionalBooleanParameter
|
55
|
+
def reference_properties(read = true)
|
56
|
+
references.filter_map do |r|
|
57
|
+
sym = reference_to_sym(r)
|
58
|
+
|
59
|
+
# All references are readable
|
60
|
+
next sym if read
|
61
|
+
|
62
|
+
# Writeable if a one type or accepting nested
|
63
|
+
association = reflect_on_association(sym)
|
64
|
+
# rubocop:todo Performance/CollectionLiteralInLoop, Layout/LineLength
|
65
|
+
if %i[has_one belongs_to].include?(association.macro) || nested_attributes_options.key?(sym) || association.class_name == "ActiveStorage::Attachment"
|
66
|
+
sym
|
67
|
+
end
|
68
|
+
# rubocop:enable Performance/CollectionLiteralInLoop, Layout/LineLength
|
69
|
+
end
|
70
|
+
end
|
71
|
+
# rubocop:enable Style/OptionalBooleanParameter
|
72
|
+
|
73
|
+
def writeable_properties
|
74
|
+
# Direct properties for this model
|
75
|
+
props = attribute_names - automatic_properties - virtual_properties - foreign_key_properties
|
76
|
+
|
77
|
+
props.concat(reference_properties(false))
|
78
|
+
|
79
|
+
props.map(&:to_s)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,228 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "js_regex"
|
4
|
+
|
5
|
+
module Rhino
|
6
|
+
module Resource
|
7
|
+
module ActiveRecordExtension
|
8
|
+
module PropertiesDescribe # rubocop:disable Metrics/ModuleLength
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
class_methods do # rubocop:disable Metrics/BlockLength
|
12
|
+
def describe_property(property) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
13
|
+
name = property_name(property).to_s
|
14
|
+
raise StandardError, "#{name} is not a valid property" unless property?(name)
|
15
|
+
|
16
|
+
{
|
17
|
+
"x-rhino-attribute": {
|
18
|
+
name:,
|
19
|
+
readableName: name.titleize,
|
20
|
+
readable: read_properties.include?(property),
|
21
|
+
creatable: create_properties.include?(property),
|
22
|
+
updatable: update_properties.include?(property)
|
23
|
+
},
|
24
|
+
readOnly: property_read_only?(name),
|
25
|
+
writeOnly: property_write_only?(name),
|
26
|
+
nullable: property_nullable?(name),
|
27
|
+
default: property_default(name)
|
28
|
+
}
|
29
|
+
.merge(property_type_and_format(property))
|
30
|
+
.merge(property_validations(property))
|
31
|
+
.deep_merge(property_overrides(property))
|
32
|
+
.compact
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
# FIXME: It can be a hash if passed in from a reference which might have something like
|
37
|
+
# rhino_references %i[{blog_posts: [:comments]}]
|
38
|
+
# but I cannot find current spot where it is used like that currently
|
39
|
+
def property_name(property)
|
40
|
+
property.is_a?(Hash) ? property.keys.first : property
|
41
|
+
end
|
42
|
+
|
43
|
+
def ref_descriptor(names)
|
44
|
+
{
|
45
|
+
type: :reference,
|
46
|
+
anyOf: names.map { |name| { :$ref => "#/components/schemas/#{name.singularize}" } }
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def property_type_and_format_attr(name) # rubocop:todo Metrics/MethodLength
|
51
|
+
atype = attribute_types[name.to_s].type
|
52
|
+
|
53
|
+
# The PG array delegates type to "subtype" which is the actual type of the array elements
|
54
|
+
if attribute_types[name.to_s].is_a? ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array
|
55
|
+
return {
|
56
|
+
type: :array,
|
57
|
+
items: {
|
58
|
+
type: atype
|
59
|
+
}
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
if %i[datetime date time].include?(atype)
|
64
|
+
return {
|
65
|
+
type: 'string',
|
66
|
+
format: atype
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
{ type: atype }
|
71
|
+
end
|
72
|
+
|
73
|
+
def nested_array_options(name)
|
74
|
+
ref_sym = name.to_sym
|
75
|
+
|
76
|
+
array_options = {}
|
77
|
+
|
78
|
+
if nested_attributes_options[ref_sym]
|
79
|
+
array_options[:creatable] = true
|
80
|
+
array_options[:updatable] = true
|
81
|
+
array_options[:destroyable] = nested_attributes_options[ref_sym][:allow_destroy]
|
82
|
+
end
|
83
|
+
|
84
|
+
{ "x-rhino-attribute-array": array_options.merge(_properties_array[ref_sym] || {}) }
|
85
|
+
end
|
86
|
+
|
87
|
+
# rubocop:todo Metrics/PerceivedComplexity
|
88
|
+
# rubocop:todo Metrics/AbcSize
|
89
|
+
def property_type_and_format_ref(name) # rubocop:todo Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/PerceivedComplexity
|
90
|
+
assoc = reflections[name]
|
91
|
+
klasses = if assoc.options[:polymorphic]
|
92
|
+
assoc.active_record.send("#{assoc.name}_types").map(&:constantize).map { |m| m.model_name.singular }
|
93
|
+
else
|
94
|
+
# FIXME: The tr hack is to match how model_name in rails handles modularized classes
|
95
|
+
[assoc.options[:class_name]&.underscore&.tr('/', '_') || name]
|
96
|
+
end
|
97
|
+
|
98
|
+
return ref_descriptor(klasses) unless reflections[name].macro == :has_many
|
99
|
+
|
100
|
+
{
|
101
|
+
type: :array,
|
102
|
+
items: ref_descriptor(klasses).merge(nested_array_options(name))
|
103
|
+
}
|
104
|
+
end
|
105
|
+
# rubocop:enable Metrics/AbcSize
|
106
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
107
|
+
|
108
|
+
def property_type_and_format(name) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
109
|
+
# Special cases
|
110
|
+
return { type: :identifier } if name == identifier_property
|
111
|
+
return { type: :string } if defined_enums.key?(name)
|
112
|
+
|
113
|
+
# FIXME: Hack for tags for now
|
114
|
+
if attribute_types.key?(name.to_s) && attribute_types[name.to_s].class.to_s == 'ActsAsTaggableOn::Taggable::TagListType'
|
115
|
+
return {
|
116
|
+
type: :array,
|
117
|
+
items: {
|
118
|
+
type: 'string'
|
119
|
+
}
|
120
|
+
}
|
121
|
+
end
|
122
|
+
|
123
|
+
# Use the attribute type if possible
|
124
|
+
return property_type_and_format_attr(name) if attribute_types.key?(name.to_s)
|
125
|
+
|
126
|
+
return property_type_and_format_ref(name) if reflections.key?(name)
|
127
|
+
|
128
|
+
# FIXME: There may be no way to reach this
|
129
|
+
# raise UnknownpropertyType
|
130
|
+
{ type: :unknown }
|
131
|
+
end
|
132
|
+
|
133
|
+
def property_overrides(property)
|
134
|
+
return {} unless _properties_overrides.key?(property)
|
135
|
+
|
136
|
+
_properties_overrides[property].deep_symbolize_keys
|
137
|
+
end
|
138
|
+
|
139
|
+
def property_validations(property) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
140
|
+
constraint_hash = {}
|
141
|
+
|
142
|
+
# https://swagger.io/specification/
|
143
|
+
|
144
|
+
validators_on(property).each do |v|
|
145
|
+
if v.is_a? ActiveModel::Validations::NumericalityValidator
|
146
|
+
if v.options.key?(:greater_than)
|
147
|
+
constraint_hash[:minimum] = v.options[:greater_than]
|
148
|
+
constraint_hash[:exclusiveMinimum] = true
|
149
|
+
end
|
150
|
+
|
151
|
+
if v.options.key?(:less_than)
|
152
|
+
constraint_hash[:maximum] = v.options[:less_than]
|
153
|
+
constraint_hash[:exclusiveMaximum] = true
|
154
|
+
end
|
155
|
+
|
156
|
+
constraint_hash[:minimum] = v.options[:greater_than_or_equal_to] if v.options.key?(:greater_than_or_equal_to)
|
157
|
+
constraint_hash[:maximum] = v.options[:less_than_or_equal_to] if v.options.key?(:less_than_or_equal_to)
|
158
|
+
|
159
|
+
if v.options.key?(:in)
|
160
|
+
constraint_hash[:minimum] = v.options[:in].min
|
161
|
+
constraint_hash[:maximum] = v.options[:in].max
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
if v.is_a? ::ActiveRecord::Validations::LengthValidator
|
166
|
+
constraint_hash[:minLength] = v.options[:minimum] || v.options[:is]
|
167
|
+
constraint_hash[:maxLength] = v.options[:maximum] || v.options[:is]
|
168
|
+
end
|
169
|
+
|
170
|
+
constraint_hash[:pattern] = JsRegex.new(v.options[:with]).source if v.is_a? ::ActiveModel::Validations::FormatValidator
|
171
|
+
|
172
|
+
constraint_hash[:enum] = v.options[:in] if v.is_a? ActiveModel::Validations::InclusionValidator
|
173
|
+
end
|
174
|
+
|
175
|
+
constraint_hash[:enum] = defined_enums[property].keys if defined_enums.key?(property)
|
176
|
+
|
177
|
+
constraint_hash.compact
|
178
|
+
end
|
179
|
+
|
180
|
+
# If there is a presence validator in the model it is not nullable.
|
181
|
+
# if there is no optional: true on an association, rails will add a
|
182
|
+
# presence validator automatically
|
183
|
+
# Otherwise check the db for the actual column or foreign key setting
|
184
|
+
# Return nil instead of false for compaction
|
185
|
+
def property_nullable?(name) # rubocop:todo Metrics/AbcSize
|
186
|
+
# Check for any presence validator
|
187
|
+
return false if validators_on(name).any?(::ActiveRecord::Validations::PresenceValidator)
|
188
|
+
|
189
|
+
# https://guides.rubyonrails.org/active_record_validations.html#numericality
|
190
|
+
# By default, numericality doesn't allow nil values. You can use allow_nil: true option to permit it.
|
191
|
+
validators_on(name).select { |v| v.is_a? ::ActiveRecord::Validations::NumericalityValidator }.each do |v|
|
192
|
+
return false unless v.options[:allow_nil]
|
193
|
+
end
|
194
|
+
|
195
|
+
name = reflections[name].foreign_key if reflections.key?(name)
|
196
|
+
|
197
|
+
# Check the column null setting
|
198
|
+
return columns_hash[name].null if columns_hash.key?(name)
|
199
|
+
|
200
|
+
true
|
201
|
+
end
|
202
|
+
|
203
|
+
# Return nil instead of false for compaction
|
204
|
+
def property_read_only?(name)
|
205
|
+
return unless read_properties.include?(name) && (create_properties.exclude?(name) && update_properties.exclude?(name))
|
206
|
+
|
207
|
+
true
|
208
|
+
end
|
209
|
+
|
210
|
+
# Return nil instead of false for compaction
|
211
|
+
def property_write_only?(name)
|
212
|
+
return unless (create_properties.include?(name) || update_properties.include?(name)) && read_properties.exclude?(name)
|
213
|
+
|
214
|
+
true
|
215
|
+
end
|
216
|
+
|
217
|
+
def property_default(name)
|
218
|
+
# FIXME: This will not handle datetime fields
|
219
|
+
# https://github.com/rails/rails/issues/27077 sets the default in the db
|
220
|
+
# but Blog.new does not set the default value like other attributes
|
221
|
+
# https://nubinary.atlassian.net/browse/NUB-298
|
222
|
+
_default_attributes[name].type_cast(_default_attributes[name].value_before_type_cast)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rhino
|
4
|
+
module Resource
|
5
|
+
module ActiveRecordExtension
|
6
|
+
module Reference
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
scope :eager_load_refs, -> { includes(klass.references) } if defined?(scope)
|
11
|
+
|
12
|
+
def references_for_serialization
|
13
|
+
serialize_references(references)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
# FIXME: Duplicated in params.rb
|
18
|
+
def reference_to_sym(reference)
|
19
|
+
reference.is_a?(Hash) ? reference.keys.first : reference
|
20
|
+
end
|
21
|
+
|
22
|
+
# FIXME: Duplicated in params.rb
|
23
|
+
def reference_from_sym(sym)
|
24
|
+
ref = try(sym)
|
25
|
+
return unless ref
|
26
|
+
|
27
|
+
# This is mostly how serializable_hash does it
|
28
|
+
# Get the first object
|
29
|
+
return ref.first if ref.respond_to?(:to_ary)
|
30
|
+
|
31
|
+
ref
|
32
|
+
end
|
33
|
+
|
34
|
+
def serialize_references(references)
|
35
|
+
hash = {}
|
36
|
+
references.each do |ref_item|
|
37
|
+
sym = reference_to_sym(ref_item)
|
38
|
+
|
39
|
+
hash[sym] = {}
|
40
|
+
hash[sym][:methods] = :display_name
|
41
|
+
hash[sym][:include] = serialize_references(ref_item[sym]) if ref_item.is_a?(Hash)
|
42
|
+
end.flatten.compact
|
43
|
+
|
44
|
+
hash
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rhino
|
4
|
+
module Resource
|
5
|
+
module ActiveRecordExtension
|
6
|
+
module Routing
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
class_methods do
|
10
|
+
def route_key
|
11
|
+
if route_singular?
|
12
|
+
model_name.singular_route_key
|
13
|
+
else
|
14
|
+
model_name.route_key
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rhino
|
4
|
+
module Resource
|
5
|
+
module ActiveRecordExtension
|
6
|
+
module Search
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
class_methods do
|
10
|
+
def rhino_search(fields, associated_fields = {})
|
11
|
+
include PgSearch::Model
|
12
|
+
# stores the searchable fields so it is possible to retrieve them
|
13
|
+
# in the open API model description
|
14
|
+
@rhino_is_searchable = fields.present? || associated_fields.present?
|
15
|
+
# FIXME: We can't autodiscover text fields because it causes a very
|
16
|
+
# slow reload in the development server with bigger apps
|
17
|
+
pg_search_scope :search_text_fields, against: fields, associated_against: associated_fields, using: { tsearch: { prefix: true } }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rhino
|
4
|
+
module Resource
|
5
|
+
module ActiveRecordExtension
|
6
|
+
module Serialization
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
def to_caching_json
|
10
|
+
serializable_hash(methods: :display_name, include: references_for_serialization)
|
11
|
+
# JSON.generate(hash)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rhino
|
4
|
+
module Resource
|
5
|
+
module ActiveRecordExtension
|
6
|
+
module SuperAdmin
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
class_methods do
|
10
|
+
def ransackable_attributes(_auth_object = nil)
|
11
|
+
authorizable_ransackable_attributes & read_properties
|
12
|
+
end
|
13
|
+
|
14
|
+
def ransackable_associations(_auth_object = nil)
|
15
|
+
authorizable_ransackable_associations & references.map(&:to_s)
|
16
|
+
end
|
17
|
+
|
18
|
+
def ransackable_filters(auth_object = nil)
|
19
|
+
ransackable_attributes(auth_object) + ransackable_associations
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../resource'
|
4
|
+
require_relative 'active_record_extension/properties'
|
5
|
+
require_relative 'active_record_extension/reference'
|
6
|
+
require_relative 'active_record_extension/describe'
|
7
|
+
require_relative 'active_record_extension/routing'
|
8
|
+
require_relative 'active_record_extension/params'
|
9
|
+
require_relative 'active_record_extension/serialization'
|
10
|
+
require_relative 'active_record_extension/search'
|
11
|
+
require_relative 'active_record_extension/super_admin'
|
12
|
+
|
13
|
+
module Rhino
|
14
|
+
module Resource
|
15
|
+
module ActiveRecordExtension
|
16
|
+
extend ActiveSupport::Concern
|
17
|
+
|
18
|
+
# Base
|
19
|
+
include Rhino::Resource
|
20
|
+
|
21
|
+
# Active Record implementations
|
22
|
+
include Rhino::Resource::ActiveRecordExtension::Properties
|
23
|
+
include Rhino::Resource::ActiveRecordExtension::Reference
|
24
|
+
include Rhino::Resource::ActiveRecordExtension::Describe
|
25
|
+
include Rhino::Resource::ActiveRecordExtension::Routing
|
26
|
+
include Rhino::Resource::ActiveRecordExtension::Params
|
27
|
+
include Rhino::Resource::ActiveRecordExtension::Serialization
|
28
|
+
include Rhino::Resource::ActiveRecordExtension::Search
|
29
|
+
include Rhino::Resource::ActiveRecordExtension::SuperAdmin
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rhino
|
4
|
+
module Resource
|
5
|
+
module ActiveRecordTree
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
# Base
|
9
|
+
include Rhino::Resource::ActiveRecordExtension unless Rhino.auto_include_active_record
|
10
|
+
|
11
|
+
included do
|
12
|
+
has_ancestry
|
13
|
+
end
|
14
|
+
|
15
|
+
class_methods do
|
16
|
+
def readable_properties
|
17
|
+
super + ["children"]
|
18
|
+
end
|
19
|
+
|
20
|
+
def describe_property(property)
|
21
|
+
return super unless property == "children"
|
22
|
+
|
23
|
+
super.deep_merge({ type: :array, items: ref_descriptor([model_name.element]) })
|
24
|
+
end
|
25
|
+
|
26
|
+
# FIXME: Need to recurse to a MAX DEPTH
|
27
|
+
def readable_params(type, refs = references)
|
28
|
+
# Remove the base children property and replace with hash
|
29
|
+
base_show = super - ["children"]
|
30
|
+
base_show + [children: [base_show]]
|
31
|
+
end
|
32
|
+
|
33
|
+
# FIXME: Need writeable params override
|
34
|
+
end
|
35
|
+
|
36
|
+
def serializable_hash(options = nil)
|
37
|
+
super(options).merge(children: arrange_serializable_with_options(options))
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
# We do this to carry things like methods all the way through the
|
42
|
+
# serialization
|
43
|
+
def arrange_serializable_with_options(options = nil)
|
44
|
+
children.arrange_serializable do |parent, _children|
|
45
|
+
parent.serializable_hash(options)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rhino
|
4
|
+
module Resource
|
5
|
+
module ActiveStorageExtension
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
# Base
|
9
|
+
include Rhino::Resource::ActiveRecordExtension unless Rhino.auto_include_active_record
|
10
|
+
|
11
|
+
included do
|
12
|
+
attribute :url
|
13
|
+
attribute :url_attachment
|
14
|
+
|
15
|
+
rhino_policy :active_storage_attachment
|
16
|
+
|
17
|
+
def url
|
18
|
+
Rails.application.routes.url_helpers.rails_blob_url(self, only_path: false)
|
19
|
+
end
|
20
|
+
|
21
|
+
def url_attachment
|
22
|
+
Rails.application.routes.url_helpers.rails_blob_url(self, only_path: false, disposition: :attachment)
|
23
|
+
end
|
24
|
+
|
25
|
+
def display_name
|
26
|
+
filename.to_s
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class_methods do
|
31
|
+
def readable_properties
|
32
|
+
super + ["signed_id"]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def serializable_hash(options = nil)
|
37
|
+
super(options).merge("signed_id" => signed_id)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rhino
|
4
|
+
module Resource
|
5
|
+
module Describe
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
delegate :describe, to: :class
|
10
|
+
end
|
11
|
+
|
12
|
+
class_methods do
|
13
|
+
def describe
|
14
|
+
raise NotImplementedError, '#describe is not implemented'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|