effective_resources 0.9.5 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 = []
|