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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/effective/crud_controller.rb +23 -546
  3. data/app/controllers/concerns/effective/crud_controller/actions.rb +289 -0
  4. data/app/controllers/concerns/effective/crud_controller/dsl.rb +81 -0
  5. data/app/controllers/concerns/effective/crud_controller/paths.rb +87 -0
  6. data/app/controllers/concerns/effective/crud_controller/permitted_params.rb +66 -0
  7. data/app/controllers/concerns/effective/crud_controller/save.rb +77 -0
  8. data/app/controllers/concerns/effective/crud_controller/submits.rb +122 -0
  9. data/app/helpers/effective_resources_helper.rb +51 -52
  10. data/app/helpers/effective_resources_private_helper.rb +69 -0
  11. data/app/models/concerns/effective_resource.rb +24 -0
  12. data/app/models/effective/model_reader.rb +29 -0
  13. data/app/models/effective/resource.rb +6 -2
  14. data/app/models/effective/resources/actions.rb +10 -10
  15. data/app/models/effective/resources/associations.rb +5 -0
  16. data/app/models/effective/resources/attributes.rb +40 -27
  17. data/app/models/effective/resources/controller.rb +81 -0
  18. data/app/models/effective/resources/forms.rb +0 -51
  19. data/app/models/effective/resources/init.rb +19 -17
  20. data/app/models/effective/resources/klass.rb +6 -8
  21. data/app/models/effective/resources/model.rb +23 -0
  22. data/app/models/effective/resources/naming.rb +7 -3
  23. data/app/models/effective/resources/paths.rb +4 -63
  24. data/app/models/effective/resources/relation.rb +4 -1
  25. data/app/models/effective/resources/sql.rb +1 -1
  26. data/app/views/application/create.js.erb +1 -1
  27. data/app/views/application/edit.html.haml +2 -2
  28. data/app/views/application/index.html.haml +1 -1
  29. data/app/views/application/member_action.js.erb +1 -1
  30. data/app/views/application/new.html.haml +3 -1
  31. data/app/views/application/show.html.haml +1 -1
  32. data/app/views/application/update.js.erb +1 -1
  33. data/app/views/effective/resource/_actions.html.haml +3 -32
  34. data/app/views/effective/resource/_actions_dropleft.html.haml +4 -26
  35. data/app/views/effective/resource/_actions_glyphicons.html.haml +4 -10
  36. data/lib/effective_resources/engine.rb +1 -0
  37. data/lib/effective_resources/version.rb +1 -1
  38. 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
- _initialize(input, namespace: namespace)
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
- @_routes ||= (
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 || written_attributes.presence)
8
+ (klass_attributes.presence || model_attributes.presence)
7
9
  end
8
10
 
9
- def attribute_names
10
- attributes.map { |attribute| attribute.name }
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.map do |name|
44
+ attributes = names.inject({}) do |h, name|
23
45
  if klass.respond_to?(:column_for_attribute) # Rails 4+
24
- Effective::Attribute.new(name, klass.column_for_attribute(name).type)
46
+ h[name.to_sym] = [klass.column_for_attribute(name).type]
25
47
  else
26
- Effective::Attribute.new(name, klass.columns_hash[name].type)
27
- end
48
+ h[name.to_sym] = [klass.columns_hash[name].type]
49
+ end; h
28
50
  end
29
51
 
30
- sort_by_written_attributes(attributes)
52
+ sort_by_model_attributes(attributes)
31
53
  end
32
54
 
33
- def belong_tos_attributes
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 written_attributes
42
- _initialize_written if @written_attributes.nil?
43
- @written_attributes
44
- end
57
+ def sort_by_model_attributes(attributes)
58
+ return attributes unless model_attributes.present?
45
59
 
46
- private
60
+ keys = model_attributes.keys
47
61
 
48
- def sort_by_written_attributes(attributes)
49
- attributes.sort do |a, b|
62
+ attributes.sort do |(a, _), (b, _)|
50
63
  index = nil
51
64
 
52
- index ||= if written_attributes.include?(a) && written_attributes.include?(b)
53
- written_attributes.index(a) <=> written_attributes.index(b)
54
- elsif written_attributes.include?(a) && !written_attributes.include?(b)
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 !written_attributes.include?(a) && written_attributes.include?(b)
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 _initialize(input, namespace: nil)
8
- @namespaces = (namespace.kind_of?(String) ? namespace.split('/') : Array(namespace)) if namespace
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
- @relation = _initialize_relation(input)
29
- @instance = input if (klass && input.instance_of?(klass))
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 = []