active_resourceable 0.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b3871f317828c3c6384cef4b7989929fc00ec45da1b6542ee7871b552b523238
4
+ data.tar.gz: 8ab803621f88abfd564262797edd806453cfba42e3878a76c3ae3ae4694a0f5d
5
+ SHA512:
6
+ metadata.gz: 0adaacc5b2a3d81e6749925a7c08673c78644e3c90635ee86d0280b225e892ffa7b9c207fe156588b327b137ea3727ffcac9a2c8e079a466ec8048bc083ab1e2
7
+ data.tar.gz: c63d668b2959dd2fbdf03915545c3796c57e87c0a0ca35865b3883dfa18bb01795b2bbc12c1f8e6352a455ad7e60248bb0b59cee30238862cbd883368732bc01
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2016 Adam Hess
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # Active Resourceable
2
+
3
+ A tiny framework built on top of Rails in order to help developers focus on writing important business logic. It should be used with hotwired
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require "bundler/setup"
2
+ require "bundler/gem_tasks"
3
+ require "rake/testtask"
4
+
5
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
6
+ load "rails/tasks/engine.rake"
7
+ load "rails/tasks/statistics.rake"
8
+
9
+ Rake::TestTask.new do |test|
10
+ test.libs << "test"
11
+ test.test_files = FileList["test/**/*_test.rb"]
12
+ test.warning = false
13
+ end
14
+
15
+ task default: :test
@@ -0,0 +1,113 @@
1
+ module ActiveResourceable::Resourceable
2
+ extend ActiveSupport::Concern
3
+ included do
4
+ end
5
+
6
+ class_methods do
7
+
8
+ def resource_model name, **opt
9
+
10
+ model_class = name.to_s.classify.constantize if name.is_a? Symbol
11
+ model_class = name.classify.constantize if name.is_a? String
12
+ model_class = name if name.is_a? Class and name.ancestors.include? ActiveRecord::Base
13
+ raise "Unable to identify resource model" if model_class.nil?
14
+ model_name = model_class.name.singularize.downcase
15
+
16
+ filter_set = opt[:filter_set]
17
+
18
+ filter_set.each do |filter_attr, filter_opt|
19
+ filter_on filter_attr, **filter_opt
20
+ end unless filter_set.nil?
21
+
22
+ sort_set = opt[:sort_set]
23
+
24
+ sort_set.each do |sort_attr, sort_opt|
25
+ sort_on sort_attr, **sort_opt
26
+ end unless sort_set.nil?
27
+
28
+ define_method("list_#{model_name.pluralize}") do
29
+ @pagy, collection = pagy(filtrate(model_class.readable_for(access_object)))
30
+ instance_variable_set :"@#{model_name.pluralize}", collection
31
+ end
32
+
33
+ define_method("show_#{model_name}") do
34
+ instance = model_class.readable_for(access_object).find(params[:id])
35
+ instance.access_object = access_object
36
+ instance_variable_set :"@#{model_name}", instance
37
+ end
38
+
39
+ define_method("new_#{model_name}") do
40
+ instance = model_class.new
41
+ instance.access_object = access_object
42
+ instance_variable_set :"@#{model_name}", instance
43
+ end
44
+
45
+ define_method("create_#{model_name}") do
46
+ instance = model_class.new
47
+ instance_variable_set :"@#{model_name}", instance
48
+ instance.access_object = access_object
49
+ instance.assign_attributes send("#{model_name}_params")
50
+ instance.save
51
+ render :new, status: :unprocessable_entity unless instance_variable_get(:"@#{model_name}").valid?
52
+ end
53
+
54
+ define_method("edit_#{model_name}") do
55
+ instance = model_class.editable_for(access_object).find(params[:id])
56
+ instance.access_object = access_object
57
+ instance_variable_set :"@#{model_name}", instance
58
+ end
59
+
60
+ define_method("update_#{model_name}") do
61
+ instance = model_class.editable_for(access_object).find(params[:id])
62
+ instance.access_object = access_object
63
+ instance_variable_set :"@#{model_name}", instance
64
+ instance.assign_attributes send("#{model_name}_params")
65
+ instance.save
66
+ render :edit, status: :unprocessable_entity unless instance_variable_get(:"@#{model_name}").valid?
67
+ end
68
+
69
+ define_method("destroy_#{model_name}") do
70
+ instance = model_class.deletable_for(access_object).find(params[:id])
71
+ instance.access_object = access_object
72
+ instance.destroy
73
+ instance_variable_set :"@#{model_name}", instance
74
+ end
75
+
76
+ define_method("#{model_name}_params") do
77
+ instance = instance_variable_get(:"@#{model_name}")
78
+ fields = instance.accessable_fields
79
+ send("#{model_name}_attrs", params.fetch(model_name, {}).permit(fields), fields)
80
+ end
81
+
82
+ alias_method "#{model_name}_args", "#{model_name}_params"
83
+
84
+ define_method("#{model_name}_attrs") do |object_params, fields|
85
+ (fields.include?(:user_id) and access_object.present?) ? object_params.merge(user_id: access_object.id) : object_params
86
+ end
87
+
88
+ define_method(:access_object) do
89
+ @access.authenticated_user
90
+ end
91
+
92
+ define_method(:filter_params) do
93
+ params.fetch(:filters, {}).permit(*filter_set.keys)
94
+ end
95
+
96
+ define_method(:sort_params) do
97
+ params.permit(:sort)
98
+ end
99
+
100
+ alias_method :user_object, :access_object
101
+
102
+ before_action :"list_#{model_name.pluralize}", only: %i[index]
103
+ before_action :"show_#{model_name}", only: %i[show]
104
+ before_action :"new_#{model_name}", only: %i[new]
105
+ before_action :"create_#{model_name}", only: %i[create]
106
+ before_action :"edit_#{model_name}", only: %i[edit]
107
+ before_action :"update_#{model_name}", only: %i[update]
108
+ before_action :"destroy_#{model_name}", only: %i[destroy]
109
+
110
+ end
111
+
112
+ end
113
+ end
@@ -0,0 +1,19 @@
1
+ class ActiveResourceable::AppFormBuilder < ActionView::Helpers::FormBuilder
2
+
3
+ Dir.glob("#{Rails.root}/app/views/app_form_builder/*").each do |view_path|
4
+ field = view_path.split('/')[-1].split('.')[0][1..-1]
5
+ define_method(field) do |*args, **kwargs, &block|
6
+ @template.render partial: "/app_form_builder/#{field}", locals: { args: args, kwargs: kwargs, block: block, field: self }
7
+ end
8
+ end
9
+
10
+ def is_required method
11
+ @object.class.validators_on(method).any? do |validator|
12
+ validator.class == ActiveRecord::Validations::PresenceValidator
13
+ end unless @object.nil?
14
+ end
15
+
16
+ def should_display_input? method, access_object
17
+ @object.accessable_fields.any? { |attr| attr.is_a?(Hash) ? attr.keys.first.to_s == "#{method}_attributes" : attr == method }
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ class ActiveResourceable::ActiveRecordInstanceMethodJob < ApplicationJob
2
+ queue_as :default
3
+
4
+ def perform(instance, method, *args)
5
+ instance.send "#{method}_sync", *args
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ class ActiveResourceable::ActiveRecordInstanceMethodJobUniqueUntilExecutingJob < ApplicationJob
2
+ queue_as :default
3
+ unique :until_executing, on_conflict: ->(job) {}
4
+
5
+ def perform(instance, method, *args)
6
+ instance.send "#{method}_sync", *args
7
+ end
8
+ end
@@ -0,0 +1,21 @@
1
+ module ActiveResourceable::Asyncable
2
+ extend ActiveSupport::Concern
3
+
4
+ class_methods do
5
+ def async_method method, options = {}
6
+ alias_method :"#{method}_sync", :"#{method}"
7
+
8
+ define_method(:"#{method}") do |*args|
9
+ ActiveResourceable::ActiveRecordInstanceMethodJob.set(**options).perform_later(self, method, *args)
10
+ end
11
+ end
12
+
13
+ def async_method_unique_until_executing method, options = {}
14
+ alias_method :"#{method}_sync", :"#{method}"
15
+
16
+ define_method(:"#{method}") do |*args|
17
+ ActiveResourceable::ActiveRecordInstanceMethodJobUniqueUntilExecutingJob.set(**options).perform_later(self, method, *args)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,26 @@
1
+ module ActiveResourceable::Governable
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+
6
+ attr_accessor :access_object
7
+
8
+ # scope :editable_for, ->(user) {
9
+ # where(false)
10
+ # }
11
+ #
12
+ # scope :readable_for, ->(user) {
13
+ # editable_for user
14
+ # }
15
+ #
16
+ # scope :deletable_for, ->(user) {
17
+ # editable_for user
18
+ # }
19
+
20
+ end
21
+
22
+ def accessable_fields
23
+ raise NotImplementedError
24
+ end
25
+
26
+ end
@@ -0,0 +1,66 @@
1
+ class ActiveResourceable::Sessionable < Module
2
+ extend ActiveSupport::Concern
3
+
4
+ def initialize(attrs)
5
+ @session_attrs = attrs
6
+ end
7
+
8
+ def included(base)
9
+ session_attrs = @session_attrs
10
+
11
+ base.class_eval do
12
+
13
+ attr_accessor :session
14
+
15
+ define_method(:session_attrs) do
16
+ session_attrs
17
+ end
18
+
19
+ def delete_session *attrs
20
+ attrs = session_attrs if attrs.empty?
21
+ attrs.each do |name, opt|
22
+ send(:"delete_#{name}")
23
+ end
24
+ end
25
+
26
+ session_attrs.each do |name, opt|
27
+
28
+ define_method(:"session_name_for_#{name}") do
29
+ "#{self.class.name.tableize}_#{name}"
30
+ end
31
+
32
+ define_method(:"modified_at_session_name_for_#{name}") do
33
+ "#{self.class.name.tableize}_#{name}_modified_at"
34
+ end
35
+
36
+ define_method(name) do
37
+ raise "Session is not set" if self.session.nil?
38
+ is_expired = send(:"is_#{name}_expired?")
39
+ is_expired ? nil : self.session[send(:"session_name_for_#{name}")]
40
+ end
41
+
42
+ define_method(:"#{name}=") do |value|
43
+ raise "Session is not set" if self.session.nil?
44
+ self.session[send(:"session_name_for_#{name}")] = value
45
+ self.session[send(:"modified_at_session_name_for_#{name}")] = Time.now
46
+ end
47
+
48
+ define_method(:"delete_#{name}") do
49
+ raise "Session is not set" if self.session.nil?
50
+ self.session.delete send(:"modified_at_session_name_for_#{name}")
51
+ self.session.delete send(:"session_name_for_#{name}")
52
+ end
53
+
54
+ define_method(:"is_#{name}_expired?") do
55
+ raise "Session is not set" if self.session.nil?
56
+ modified_at = self.session[send(:"modified_at_session_name_for_#{name}")]
57
+ (not opt[:expire_after].nil? and not modified_at.nil?) ? modified_at.to_datetime + opt[:expire_after] < Time.now : false
58
+ end
59
+
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+
66
+ end
@@ -0,0 +1,40 @@
1
+ require "rails/engine"
2
+ require "pagy"
3
+ require "procore-sift"
4
+
5
+ module ActiveResourceable
6
+ class Engine < ::Rails::Engine
7
+ isolate_namespace ActiveResourceable
8
+ engine_name "active_resourceable"
9
+ config.eager_load_namespaces << ActiveResourceable
10
+ config.active_resourceable = ActiveSupport::OrderedOptions.new
11
+ config.autoload_once_paths = %W(
12
+ #{root}/app/channels
13
+ #{root}/app/controllers
14
+ #{root}/app/controllers/concerns
15
+ #{root}/app/helpers
16
+ #{root}/app/models
17
+ #{root}/app/models/concerns
18
+ #{root}/app/jobs
19
+ )
20
+
21
+ initializer "active_resourceable.resourceable", before: :load_config_initializers do
22
+ ActiveSupport.on_load(:action_controller_base) do
23
+ default_form_builder ActiveResourceable::AppFormBuilder
24
+ include Pagy::Backend
25
+ include Sift
26
+ include ActiveResourceable::Resourceable
27
+ end
28
+ end
29
+
30
+ initializer "active_resourceable.models_concerns" do
31
+ ActiveSupport.on_load(:active_record) do
32
+ include ActiveResourceable::Governable
33
+ include ActiveResourceable::Asyncable
34
+ scope :unpaginate, ->() {
35
+ offset(nil).limit(nil)
36
+ }
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveResourceable
2
+ VERSION = "0.0.0".freeze
3
+ end
@@ -0,0 +1,5 @@
1
+ require "active_resourceable/engine"
2
+
3
+ module ActiveResourceable
4
+ extend ActiveSupport::Autoload
5
+ end
metadata ADDED
@@ -0,0 +1,155 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_resourceable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Abdullah Dahmash
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-02-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 7.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 7.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: actionpack
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 7.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 7.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: activejob
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 7.0.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 7.0.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: railties
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 7.0.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 7.0.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: procore-sift
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 0.16.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.16.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: pagy
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: 6.0.0
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: 6.0.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: activejob-uniqueness
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: 0.2.4
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: 0.2.4
111
+ description: A tiny framework built on top of Rails in order to help developers focus
112
+ on writing important business logic. It should be used with hotwired
113
+ email:
114
+ - a@and.sa
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - MIT-LICENSE
120
+ - README.md
121
+ - Rakefile
122
+ - app/controllers/concerns/active_resourceable/resourceable.rb
123
+ - app/helpers/active_resourceable/app_form_builder.rb
124
+ - app/jobs/active_resourceable/active_record_instance_method_job.rb
125
+ - app/jobs/active_resourceable/active_record_instance_method_job_unique_until_executing_job.rb
126
+ - app/models/concerns/active_resourceable/asyncable.rb
127
+ - app/models/concerns/active_resourceable/governable.rb
128
+ - app/models/concerns/active_resourceable/sessionable.rb
129
+ - lib/active_resourceable.rb
130
+ - lib/active_resourceable/engine.rb
131
+ - lib/active_resourceable/version.rb
132
+ homepage: https://github.com/AbdullahDahmash/active_resourceable
133
+ licenses:
134
+ - MIT
135
+ metadata: {}
136
+ post_install_message:
137
+ rdoc_options: []
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: 2.3.0
145
+ required_rubygems_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ requirements: []
151
+ rubygems_version: 3.3.5
152
+ signing_key:
153
+ specification_version: 4
154
+ summary: Active Resourceable
155
+ test_files: []