effective_resources 0.9.5 → 0.10.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/app/controllers/concerns/effective/crud_controller.rb +23 -546
- data/app/controllers/concerns/effective/crud_controller/actions.rb +289 -0
- data/app/controllers/concerns/effective/crud_controller/dsl.rb +81 -0
- data/app/controllers/concerns/effective/crud_controller/paths.rb +87 -0
- data/app/controllers/concerns/effective/crud_controller/permitted_params.rb +66 -0
- data/app/controllers/concerns/effective/crud_controller/save.rb +77 -0
- data/app/controllers/concerns/effective/crud_controller/submits.rb +122 -0
- data/app/helpers/effective_resources_helper.rb +51 -52
- data/app/helpers/effective_resources_private_helper.rb +69 -0
- data/app/models/concerns/effective_resource.rb +24 -0
- data/app/models/effective/model_reader.rb +29 -0
- data/app/models/effective/resource.rb +6 -2
- data/app/models/effective/resources/actions.rb +10 -10
- data/app/models/effective/resources/associations.rb +5 -0
- data/app/models/effective/resources/attributes.rb +40 -27
- data/app/models/effective/resources/controller.rb +81 -0
- data/app/models/effective/resources/forms.rb +0 -51
- data/app/models/effective/resources/init.rb +19 -17
- data/app/models/effective/resources/klass.rb +6 -8
- data/app/models/effective/resources/model.rb +23 -0
- data/app/models/effective/resources/naming.rb +7 -3
- data/app/models/effective/resources/paths.rb +4 -63
- data/app/models/effective/resources/relation.rb +4 -1
- data/app/models/effective/resources/sql.rb +1 -1
- data/app/views/application/create.js.erb +1 -1
- data/app/views/application/edit.html.haml +2 -2
- data/app/views/application/index.html.haml +1 -1
- data/app/views/application/member_action.js.erb +1 -1
- data/app/views/application/new.html.haml +3 -1
- data/app/views/application/show.html.haml +1 -1
- data/app/views/application/update.js.erb +1 -1
- data/app/views/effective/resource/_actions.html.haml +3 -32
- data/app/views/effective/resource/_actions_dropleft.html.haml +4 -26
- data/app/views/effective/resource/_actions_glyphicons.html.haml +4 -10
- data/lib/effective_resources/engine.rb +1 -0
- data/lib/effective_resources/version.rb +1 -1
- metadata +14 -3
@@ -0,0 +1,24 @@
|
|
1
|
+
# EffectiveResource
|
2
|
+
#
|
3
|
+
# Mark your model with 'effective_resource'
|
4
|
+
|
5
|
+
module EffectiveResource
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
module ActiveRecord
|
9
|
+
def effective_resource(options = nil, &block)
|
10
|
+
return @_effective_resource unless block_given?
|
11
|
+
|
12
|
+
include ::EffectiveResource
|
13
|
+
@_effective_resource = Effective::Resource.new(self, &block)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
included do
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Effective
|
2
|
+
class ModelReader
|
3
|
+
DATATYPES = [:binary, :boolean, :date, :datetime, :decimal, :float, :hstore, :inet, :integer, :string, :text]
|
4
|
+
|
5
|
+
attr_reader :attributes
|
6
|
+
|
7
|
+
def initialize(&block)
|
8
|
+
@attributes = {}
|
9
|
+
instance_exec(&block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(m, *args, &block)
|
13
|
+
raise "#{m} has already been defined" if attributes[m]
|
14
|
+
|
15
|
+
if m == :timestamps
|
16
|
+
attributes[:created_at] = [:datetime]
|
17
|
+
attributes[:updated_at] = [:datetime]
|
18
|
+
return
|
19
|
+
end
|
20
|
+
|
21
|
+
unless DATATYPES.include?(args.first)
|
22
|
+
raise "expected first argument to be a datatype. Try name :string"
|
23
|
+
end
|
24
|
+
|
25
|
+
attributes[m] = args
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -3,18 +3,22 @@ module Effective
|
|
3
3
|
include Effective::Resources::Actions
|
4
4
|
include Effective::Resources::Associations
|
5
5
|
include Effective::Resources::Attributes
|
6
|
+
include Effective::Resources::Controller
|
6
7
|
include Effective::Resources::Init
|
7
8
|
include Effective::Resources::Instance
|
8
9
|
include Effective::Resources::Forms
|
9
10
|
include Effective::Resources::Klass
|
11
|
+
include Effective::Resources::Model
|
10
12
|
include Effective::Resources::Naming
|
11
13
|
include Effective::Resources::Paths
|
12
14
|
include Effective::Resources::Relation
|
13
15
|
include Effective::Resources::Sql
|
14
16
|
|
15
17
|
# post, Post, Admin::Post, admin::Post, admin/posts, admin/post, admin/effective::post
|
16
|
-
def initialize(input, namespace: nil)
|
17
|
-
|
18
|
+
def initialize(input, namespace: nil, &block)
|
19
|
+
_initialize_input(input, namespace: namespace)
|
20
|
+
_initialize_model(&block) if block_given?
|
21
|
+
self
|
18
22
|
end
|
19
23
|
|
20
24
|
def to_s
|
@@ -5,8 +5,8 @@ module Effective
|
|
5
5
|
# This was written for the Edit actions fallback templates and Datatables
|
6
6
|
# Effective::Resource.new('admin/posts').routes[:index]
|
7
7
|
def routes
|
8
|
-
@
|
9
|
-
matches = [[namespace, plural_name].compact.join('/'), [namespace, name].compact.join('/')]
|
8
|
+
@routes ||= (
|
9
|
+
matches = [[namespace, plural_name].compact.join('/'.freeze), [namespace, name].compact.join('/'.freeze)]
|
10
10
|
|
11
11
|
routes_engine.routes.routes.select do |route|
|
12
12
|
matches.any? { |match| match == route.defaults[:controller] }
|
@@ -19,7 +19,7 @@ module Effective
|
|
19
19
|
# Effective::Resource.new('effective/order', namespace: :admin)
|
20
20
|
def routes_engine
|
21
21
|
case class_name
|
22
|
-
when 'Effective::Order'
|
22
|
+
when 'Effective::Order'.freeze
|
23
23
|
EffectiveOrders::Engine
|
24
24
|
else
|
25
25
|
Rails.application
|
@@ -30,7 +30,7 @@ module Effective
|
|
30
30
|
# This will return empty for create, update and destroy
|
31
31
|
def action_path_helper(action)
|
32
32
|
return unless routes[action]
|
33
|
-
return (routes[action].name + '_path') if routes[action].name.present?
|
33
|
+
return (routes[action].name + '_path'.freeze) if routes[action].name.present?
|
34
34
|
end
|
35
35
|
|
36
36
|
# Effective::Resource.new('admin/posts').action_path(:edit, Post.last) => '/admin/posts/3/edit'
|
@@ -107,29 +107,29 @@ module Effective
|
|
107
107
|
|
108
108
|
# Same as controller_path in the view
|
109
109
|
def controller_path
|
110
|
-
[namespace, plural_name].compact * '/'
|
110
|
+
[namespace, plural_name].compact * '/'.freeze
|
111
111
|
end
|
112
112
|
|
113
113
|
private
|
114
114
|
|
115
115
|
def is_member_route?(route)
|
116
|
-
(route.path.required_names || []).include?('id')
|
116
|
+
(route.path.required_names || []).include?('id'.freeze)
|
117
117
|
end
|
118
118
|
|
119
119
|
def is_collection_route?(route)
|
120
|
-
(route.path.required_names || []).include?('id') == false
|
120
|
+
(route.path.required_names || []).include?('id'.freeze) == false
|
121
121
|
end
|
122
122
|
|
123
123
|
def is_get_route?(route)
|
124
|
-
route.verb.to_s.include?('GET')
|
124
|
+
route.verb.to_s.include?('GET'.freeze)
|
125
125
|
end
|
126
126
|
|
127
127
|
def is_delete_route?(route)
|
128
|
-
route.verb.to_s.include?('DELETE')
|
128
|
+
route.verb.to_s.include?('DELETE'.freeze)
|
129
129
|
end
|
130
130
|
|
131
131
|
def is_post_route?(route)
|
132
|
-
['POST', 'PUT', 'PATCH'].any? { |verb| route.verb == verb }
|
132
|
+
['POST', 'PUT', 'PATCH'].freeze.any? { |verb| route.verb == verb }
|
133
133
|
end
|
134
134
|
end
|
135
135
|
end
|
@@ -11,6 +11,11 @@ module Effective
|
|
11
11
|
@belong_tos ||= klass.reflect_on_all_associations(:belongs_to)
|
12
12
|
end
|
13
13
|
|
14
|
+
# author_id, post_id
|
15
|
+
def belong_tos_ids
|
16
|
+
belong_tos.map { |ass| (ass.options[:foreign_key] || "#{ass.name}_id").to_sym }
|
17
|
+
end
|
18
|
+
|
14
19
|
def has_ones
|
15
20
|
return [] unless klass.respond_to?(:reflect_on_all_associations)
|
16
21
|
@has_ones ||= klass.reflect_on_all_associations(:has_one)
|
@@ -2,12 +2,34 @@ module Effective
|
|
2
2
|
module Resources
|
3
3
|
module Attributes
|
4
4
|
|
5
|
+
# This is the attributes as defined by ActiveRecord table
|
6
|
+
# { :name => [:string], ... }
|
5
7
|
def attributes
|
6
|
-
(klass_attributes.presence ||
|
8
|
+
(klass_attributes.presence || model_attributes.presence)
|
7
9
|
end
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
+
# The attributes for each belongs_to
|
12
|
+
# { :client_id => [:integer], ... }
|
13
|
+
def belong_tos_attributes
|
14
|
+
belong_tos.inject({}) do |h, ass|
|
15
|
+
unless ass.foreign_key == 'site_id' && ass.respond_to?(:acts_as_site_specific)
|
16
|
+
h[ass.foreign_key.to_sym] = [:integer]
|
17
|
+
end; h
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# This is the attributes as defined by the effective_resources do .. end block
|
22
|
+
# { :name => [:string, { permitted: false }], ... }
|
23
|
+
def model_attributes
|
24
|
+
model ? model.attributes : {}
|
25
|
+
end
|
26
|
+
|
27
|
+
# Used by effective_crud_controller to generate the permitted params
|
28
|
+
def permitted_attributes
|
29
|
+
id = {klass.primary_key.to_sym => [:integer]}
|
30
|
+
bts = belong_tos_ids.inject({}) { |h, ass| h[ass] = [:integer]; h }
|
31
|
+
|
32
|
+
id.merge(bts).merge(model_attributes)
|
11
33
|
end
|
12
34
|
|
13
35
|
# All attributes from the klass, sorted as per attributes block.
|
@@ -19,46 +41,37 @@ module Effective
|
|
19
41
|
names = attributes.keys - belong_tos.map { |reference| reference.foreign_key }
|
20
42
|
names = names - [klass.primary_key, 'created_at', 'updated_at'] unless all
|
21
43
|
|
22
|
-
attributes = names.
|
44
|
+
attributes = names.inject({}) do |h, name|
|
23
45
|
if klass.respond_to?(:column_for_attribute) # Rails 4+
|
24
|
-
|
46
|
+
h[name.to_sym] = [klass.column_for_attribute(name).type]
|
25
47
|
else
|
26
|
-
|
27
|
-
end
|
48
|
+
h[name.to_sym] = [klass.columns_hash[name].type]
|
49
|
+
end; h
|
28
50
|
end
|
29
51
|
|
30
|
-
|
52
|
+
sort_by_model_attributes(attributes)
|
31
53
|
end
|
32
54
|
|
33
|
-
|
34
|
-
belong_tos.map do |reference|
|
35
|
-
unless reference.foreign_key == 'site_id' && klass.respond_to?(:acts_as_site_specific)
|
36
|
-
Effective::Attribute.new(reference.foreign_key, :integer)
|
37
|
-
end
|
38
|
-
end.compact.sort
|
39
|
-
end
|
55
|
+
private
|
40
56
|
|
41
|
-
def
|
42
|
-
|
43
|
-
@written_attributes
|
44
|
-
end
|
57
|
+
def sort_by_model_attributes(attributes)
|
58
|
+
return attributes unless model_attributes.present?
|
45
59
|
|
46
|
-
|
60
|
+
keys = model_attributes.keys
|
47
61
|
|
48
|
-
|
49
|
-
attributes.sort do |a, b|
|
62
|
+
attributes.sort do |(a, _), (b, _)|
|
50
63
|
index = nil
|
51
64
|
|
52
|
-
index ||= if
|
53
|
-
|
54
|
-
elsif
|
65
|
+
index ||= if model_attributes.key?(a) && model_attributes.key?(b)
|
66
|
+
keys.index(a) <=> keys.index(b)
|
67
|
+
elsif model_attributes.key?(a) && !model_attributes.include?(b)
|
55
68
|
-1
|
56
|
-
elsif !
|
69
|
+
elsif !model_attributes.key?(a) && model_attributes.key?(b)
|
57
70
|
1
|
58
71
|
end
|
59
72
|
|
60
73
|
index || a <=> b
|
61
|
-
end
|
74
|
+
end.to_h
|
62
75
|
end
|
63
76
|
|
64
77
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Effective
|
2
|
+
module Resources
|
3
|
+
module Controller
|
4
|
+
|
5
|
+
# Used by effective_form_submit
|
6
|
+
# The actions we would use to commit. For link_to
|
7
|
+
# { 'Save': { action: :save }, 'Continue': { action: :save }, 'Add New': { action: :save }, 'Approve': { action: :approve } }
|
8
|
+
# Saves a list of commit actions...
|
9
|
+
def submits
|
10
|
+
{}.tap do |submits|
|
11
|
+
if (actions.find { |a| a == :create } || actions.find { |a| a == :update })
|
12
|
+
submits['Save'] = { action: :save, default: true }
|
13
|
+
end
|
14
|
+
|
15
|
+
if actions.find { |a| a == :index }
|
16
|
+
submits['Continue'] = { action: :save, redirect: :index, default: true }
|
17
|
+
end
|
18
|
+
|
19
|
+
if actions.find { |a| a == :new }
|
20
|
+
submits['Add New'] = { action: :save, redirect: :new, default: true }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def buttons
|
26
|
+
{}.tap do |buttons|
|
27
|
+
member_get_actions.each do |action| # default true means it will be overwritten by dsl methods
|
28
|
+
buttons[action.to_s.titleize] = { action: action, default: true }
|
29
|
+
end
|
30
|
+
|
31
|
+
(member_post_actions - crud_actions).each do |action| # default true means it will be overwritten by dsl methods
|
32
|
+
buttons[action.to_s.titleize] = { action: action, default: true }
|
33
|
+
end
|
34
|
+
|
35
|
+
member_delete_actions.each do |action|
|
36
|
+
buttons[action == :destroy ? 'Delete' : action.to_s.titleize] = { action: action, default: true }
|
37
|
+
end
|
38
|
+
|
39
|
+
if collection_get_actions.find { |a| a == :index }
|
40
|
+
buttons["All #{human_plural_name}".titleize] = { action: :index, default: true }
|
41
|
+
end
|
42
|
+
|
43
|
+
if collection_get_actions.find { |a| a == :new }
|
44
|
+
buttons["New #{human_name}".titleize] = { action: :new, default: true }
|
45
|
+
end
|
46
|
+
|
47
|
+
(collection_get_actions - crud_actions).each do |action|
|
48
|
+
buttons[action.to_s.titleize] = { action: action, default: true }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# This is the fallback for render_resource_actions when no actions are specified
|
54
|
+
# It is used by datatables
|
55
|
+
def resource_actions
|
56
|
+
{}.tap do |actions|
|
57
|
+
(member_get_actions & crud_actions).each do |action|
|
58
|
+
actions[action.to_s.titleize] = { action: action, default: true }
|
59
|
+
end
|
60
|
+
|
61
|
+
(member_get_actions - crud_actions).each do |action|
|
62
|
+
actions[action.to_s.titleize] = { action: action, default: true }
|
63
|
+
end
|
64
|
+
|
65
|
+
(member_post_actions - crud_actions).each do |action|
|
66
|
+
actions[action.to_s.titleize] = { action: action, default: true }
|
67
|
+
end
|
68
|
+
|
69
|
+
member_delete_actions.each do |action|
|
70
|
+
actions[action == :destroy ? 'Delete' : action.to_s.titleize] = { action: action, default: true }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def ons
|
76
|
+
{}
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -2,57 +2,6 @@ module Effective
|
|
2
2
|
module Resources
|
3
3
|
module Forms
|
4
4
|
|
5
|
-
# Used by effective_form_submit
|
6
|
-
# The actions we would use to commit. For link_to
|
7
|
-
# { 'Save': { action: :save }, 'Continue': { action: :save }, 'Add New': { action: :save }, 'Approve': { action: :approve } }
|
8
|
-
# Saves a list of commit actions...
|
9
|
-
def submits
|
10
|
-
@submits ||= {}.tap do |submits|
|
11
|
-
if (actions.find { |a| a == :create } || actions.find { |a| a == :update })
|
12
|
-
submits['Save'] = { action: :save, default: true, class: 'btn btn-primary' }
|
13
|
-
end
|
14
|
-
|
15
|
-
(member_post_actions - crud_actions).each do |action| # default true means it will be overwritten by dsl methods
|
16
|
-
submits[action.to_s.titleize] = { action: action, default: true, class: 'btn btn-primary' }
|
17
|
-
end
|
18
|
-
|
19
|
-
if actions.find { |a| a == :index }
|
20
|
-
submits['Continue'] = { action: :save, default: true, redirect: :index }
|
21
|
-
end
|
22
|
-
|
23
|
-
if actions.find { |a| a == :new }
|
24
|
-
submits['Add New'] = { action: :save, default: true, redirect: :new }
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
# Here we look at all available (class level) member actions, see which ones apply to the current resource
|
30
|
-
# This feeds into the helper simple_form_submit(f)
|
31
|
-
# Returns a Hash of {'Save': {class: 'btn btn-primary'}, 'Approve': {class: 'btn btn-secondary'}}
|
32
|
-
def submits_for(obj, controller:)
|
33
|
-
submits.select do |commit, args|
|
34
|
-
args[:class] = args[:class].to_s
|
35
|
-
|
36
|
-
action = (args[:action] == :save ? (obj.new_record? ? :create : :update) : args[:action])
|
37
|
-
|
38
|
-
(args.key?(:if) ? obj.instance_exec(&args[:if]) : true) &&
|
39
|
-
(args.key?(:unless) ? !obj.instance_exec(&args[:unless]) : true) &&
|
40
|
-
EffectiveResources.authorized?(controller, action, obj)
|
41
|
-
end.transform_values.with_index do |opts, index|
|
42
|
-
if opts[:class].blank?
|
43
|
-
if index == 0
|
44
|
-
opts[:class] = 'btn btn-primary'
|
45
|
-
elsif defined?(EffectiveBootstrap)
|
46
|
-
opts[:class] = 'btn btn-secondary'
|
47
|
-
else
|
48
|
-
opts[:class] = 'btn btn-default'
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
opts.except(:action, :default, :if, :unless, :redirect)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
5
|
# Used by datatables
|
57
6
|
def search_form_field(name, type = nil)
|
58
7
|
case (type || sql_type(name))
|
@@ -4,12 +4,14 @@ module Effective
|
|
4
4
|
|
5
5
|
private
|
6
6
|
|
7
|
-
def
|
8
|
-
@
|
7
|
+
def _initialize_input(input, namespace: nil)
|
8
|
+
@initialized_name = input
|
9
9
|
|
10
10
|
@model_klass = case input
|
11
11
|
when String, Symbol
|
12
12
|
_klass_by_name(input)
|
13
|
+
when Class
|
14
|
+
input
|
13
15
|
when ActiveRecord::Relation
|
14
16
|
input.klass
|
15
17
|
when (ActiveRecord::Reflection::AbstractReflection rescue :nil)
|
@@ -19,14 +21,25 @@ module Effective
|
|
19
21
|
when ActionDispatch::Journey::Route
|
20
22
|
@initialized_name = input.defaults[:controller]
|
21
23
|
_klass_by_name(input.defaults[:controller])
|
22
|
-
when Class
|
23
|
-
input
|
24
24
|
when nil ; raise 'expected a string or class'
|
25
25
|
else ; _klass_by_name(input.class.name)
|
26
26
|
end
|
27
27
|
|
28
|
-
|
29
|
-
|
28
|
+
if namespace
|
29
|
+
@namespaces = (namespace.kind_of?(String) ? namespace.split('/') : Array(namespace))
|
30
|
+
end
|
31
|
+
|
32
|
+
if input.kind_of?(ActiveRecord::Relation)
|
33
|
+
@relation = input
|
34
|
+
end
|
35
|
+
|
36
|
+
if input.kind_of?(ActiveRecord::Reflection::MacroReflection) && input.scope
|
37
|
+
@relation = klass.where(nil).merge(input.scope)
|
38
|
+
end
|
39
|
+
|
40
|
+
if klass && input.instance_of?(klass)
|
41
|
+
@instance = input
|
42
|
+
end
|
30
43
|
end
|
31
44
|
|
32
45
|
def _klass_by_name(input)
|
@@ -50,17 +63,6 @@ module Effective
|
|
50
63
|
nil
|
51
64
|
end
|
52
65
|
|
53
|
-
def _initialize_relation(input)
|
54
|
-
return nil unless klass && klass.respond_to?(:where)
|
55
|
-
|
56
|
-
case input
|
57
|
-
when ActiveRecord::Relation
|
58
|
-
input
|
59
|
-
when ActiveRecord::Reflection::MacroReflection
|
60
|
-
klass.where(nil).merge(input.scope) if input.scope
|
61
|
-
end || klass.where(nil)
|
62
|
-
end
|
63
|
-
|
64
66
|
# Lazy initialized
|
65
67
|
def _initialize_written
|
66
68
|
@written_attributes = []
|