effective_resources 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c009d7e3ecdcedf8d6237f147268f258cb1def21
4
- data.tar.gz: e626bb392c315716a7f8805a0ec43fee41aa2543
3
+ metadata.gz: c81fb1d207d2a80098702d0539cce1304db0388c
4
+ data.tar.gz: d588a94d7b30875b233d130c798b469e289835ae
5
5
  SHA512:
6
- metadata.gz: 22d8d71e5e635de0dac748d98a99cabbcefb1a741c0705489de856274a5ce0903e355ffb0d10de19751e6e8132988dc785a1ba8ef077f47c319d232a95888b48
7
- data.tar.gz: f37f7fec56ae18b3dd8e86f0cc6ff43127fd0a7150231cd2be2e43a7e42456ca8ba5d0db6089de3fcd31e7aff698ba9b28973e48c884b70d1e108447edbfbf67
6
+ metadata.gz: 3bffe8691feaf152e672de6fc5239fea4aa155b67fbc776d1a408388e1554290b14cdb53da6bd79346725466a167c4b5c9f003e6121e150c491492e924d659c5
7
+ data.tar.gz: 77d22eed11ba2e26b1b7a1082141755c64b99fe7a9c5dd7b42be97a81ee1b72e7ad0a2b907461c767d7e9e6e943eab207ba1e78b0c4e08d9b24c16cd8f2e3a8c
data/README.md CHANGED
@@ -37,11 +37,11 @@ end
37
37
 
38
38
  Implements the 7 RESTful actions: `index`, `new`, `create`, `show`, `edit`, `update`, `destroy`.
39
39
 
40
- # Loads an appropriate `@post` type instance
41
- # Sets a `@page_title` (effective_pages).
42
- # Calls authorize as per the configured `EffectiveResources.authorization_method` (flow through to CanCan or Pundit)
43
- # Does the create/update save
44
- # Sets a `flash[:success]` and redirects on success, or sets a `flash.now[:danger]` and renders on error.
40
+ - Loads an appropriate `@post` type instance
41
+ - Sets a `@page_title` (effective_pages).
42
+ - Calls authorize as per the configured `EffectiveResources.authorization_method` (flow through to CanCan or Pundit)
43
+ - Does the create/update save
44
+ - Sets a `flash[:success]` and redirects on success, or sets a `flash.now[:danger]` and renders on error.
45
45
 
46
46
  ## Helpers
47
47
 
@@ -9,11 +9,14 @@ module Effective
9
9
  end
10
10
 
11
11
  def index
12
- @page_title ||= resource_name.pluralize.titleize
12
+ @page_title ||= resource_plural_name.titleize
13
13
  EffectiveResources.authorized?(self, :index, resource_class)
14
14
 
15
15
  self.resources ||= resource_class.all
16
- @datatable ||= resource_datatable_class.new(params[:scopes])
16
+
17
+ if resource_datatable_class
18
+ @datatable ||= resource_datatable_class.new(params[:scopes])
19
+ end
17
20
  end
18
21
 
19
22
  def new
@@ -79,7 +82,7 @@ module Effective
79
82
  flash[:danger] = "Unable to delete #{resource_name}: #{resource.errors.full_messages.to_sentence}"
80
83
  end
81
84
 
82
- request.referer.present? ? redirect_to(request.referer) : redirect_to(resources_path)
85
+ request.referer.present? ? redirect_to(request.referer) : redirect_to(send(resource_index_path))
83
86
  end
84
87
 
85
88
  protected
@@ -93,66 +96,51 @@ module Effective
93
96
  end
94
97
 
95
98
  def resources # @things
96
- send(:instance_variable_get, "@#{resource_name.pluralize}")
99
+ send(:instance_variable_get, "@#{resource_plural_name}")
97
100
  end
98
101
 
99
102
  def resources=(instance)
100
- send(:instance_variable_set, "@#{resource_name.pluralize}", instance)
103
+ send(:instance_variable_set, "@#{resource_plural_name}", instance)
101
104
  end
102
105
 
103
106
  def resource_class # Thing
104
- resource_namespaced_name.classify.safe_constantize || resource_name.classify.constantize
107
+ effective_resource.klass
105
108
  end
106
109
 
107
110
  def resource_name # 'thing'
108
- controller_path.split('/').last.singularize
111
+ effective_resource.name
109
112
  end
110
113
 
111
- def resource_namespace # ['admin']
112
- controller_path.split('/')[0..-2].presence
114
+ def resource_plural_name # 'things'
115
+ effective_resource.plural_name
113
116
  end
114
117
 
115
- def resource_namespaced_name # 'Admin::Thing'
116
- ([resource_namespace, resource_name].compact * '/').singularize.camelize
117
- end
118
-
119
- def resource_datatable_class
120
- if defined?(EffectiveDatatables)
121
- "#{resource_namespaced_name.pluralize}Datatable".safe_constantize ||
122
- "#{resource_name.pluralize.camelize}Datatable".safe_constantize ||
123
- "Effective::Datatables::#{resource_namespaced_name.pluralize}".safe_constantize ||
124
- "Effective::Datatables::#{resource_name.pluralize.camelize}".safe_constantize
125
- end
118
+ def resource_datatable_class # ThingsDatatable
119
+ effective_resource.datatable_klass
126
120
  end
127
121
 
128
122
  def resource_params_method_name
129
- ["#{resource_name}_params", "#{resource_name.pluralize}_params", 'permitted_params'].find { |name| respond_to?(name, true) } || 'params'
130
- end
131
-
132
- def resources_path # /admin/things
133
- send([resource_namespace, resource_name.pluralize, 'path'].compact.join('_'))
123
+ ["#{resource_name}_params", "#{resource_plural_name}_params", 'permitted_params'].find { |name| respond_to?(name, true) } || 'params'
134
124
  end
135
125
 
136
- def new_resource_path # /admin/things/new
137
- send(['new', resource_namespace, resource_name, 'path'].compact.join('_'))
138
- end
139
-
140
- def edit_resource_path # /admin/things/1/edit
141
- send(['edit', resource_namespace, resource_name, 'path'].compact.join('_'), resource)
142
- end
143
-
144
- def resource_path # /admin/things/1
145
- send([resource_namespace, resource_name, 'path'].compact.join('_'), resource)
126
+ def resource_index_path
127
+ effective_resource.index_path
146
128
  end
147
129
 
148
130
  def resource_redirect_path
149
131
  case params[:commit].to_s
150
- when 'Save' ; edit_resource_path
151
- when 'Save and Continue' ; resources_path
152
- when 'Save and Add New' ; new_resource_path
153
- else resource_path
132
+ when 'Save' ; send(effective_resource.edit_path, resource)
133
+ when 'Save and Continue' ; send(effective_resource.index_path)
134
+ when 'Save and Add New' ; send(effective_resource.new_path)
135
+ else send(effective_resource.show_path, resource)
154
136
  end
155
137
  end
156
138
 
139
+ private
140
+
141
+ def effective_resource
142
+ @_effective_resource ||= Effective::Resource.new(controller_path)
143
+ end
144
+
157
145
  end
158
146
  end
@@ -0,0 +1,59 @@
1
+ module Effective
2
+ class Attribute
3
+ attr_accessor :name, :type
4
+
5
+ REGEX = /^\W*(\w+)\W*:(\w+)/
6
+
7
+ def self.parse(input)
8
+ input = input.to_s
9
+
10
+ if (scanned = input.scan(REGEX).first).present?
11
+ new(*scanned)
12
+ elsif input.start_with?('#')
13
+ new(nil)
14
+ else
15
+ new(input)
16
+ end
17
+ end
18
+
19
+ def initialize(name, type = nil, options = {})
20
+ @name = name.to_s
21
+ @type = (type.presence || :string).to_sym
22
+ @options = options
23
+ end
24
+
25
+ def to_s
26
+ name
27
+ end
28
+
29
+ def field_type
30
+ @field_type ||= case type
31
+ when :integer then :number_field
32
+ when :float, :decimal then :text_field
33
+ when :time then :time_select
34
+ when :datetime, :timestamp then :datetime_select
35
+ when :date then :date_select
36
+ when :text then :text_area
37
+ when :boolean then :check_box
38
+ else :text_field
39
+ end
40
+ end
41
+
42
+ def present?
43
+ name.present?
44
+ end
45
+
46
+ def human_name
47
+ name.humanize
48
+ end
49
+
50
+ def <=>(other)
51
+ name <=> other.name
52
+ end
53
+
54
+ def ==(other)
55
+ name == other.name && type == other.type
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,62 @@
1
+ module Effective
2
+ class CodeReader
3
+ attr_reader :lines
4
+
5
+ def initialize(filename, &block)
6
+ @lines = File.open(filename).readlines
7
+ block.call(self) if block_given?
8
+ end
9
+
10
+ # Iterate over the lines with a depth, and passed the stripped line to the passed block
11
+ def each_with_depth(from: nil, to: nil, &block)
12
+ Array(lines).each_with_index do |line, index|
13
+ next if index < (from || 0)
14
+
15
+ depth = line.length - line.lstrip.length
16
+ block.call(line.strip, depth, index)
17
+
18
+ break if to == index
19
+ end
20
+
21
+ nil
22
+ end
23
+
24
+ # Returns the index of the first line where the passed block returns true
25
+ def index(from: nil, to: nil, &block)
26
+ each_with_depth(from: from, to: to) do |line, depth, index|
27
+ return index if block.call(line, depth, index)
28
+ end
29
+ end
30
+
31
+ # Returns the stripped contents of the line in which the passed block returns true
32
+ def first(from: nil, to: nil, &block)
33
+ each_with_depth(from: from, to: to) do |line, depth, index|
34
+ return line if block.call(line, depth, index)
35
+ end
36
+ end
37
+ alias_method :find, :first
38
+
39
+ # Returns the stripped contents of the last line where the passed block returns true
40
+ def last(from: nil, to: nil, &block)
41
+ retval = nil
42
+
43
+ each_with_depth(from: from, to: nil) do |line, depth, index|
44
+ retval = line if block.call(line, depth, index)
45
+ end
46
+
47
+ retval
48
+ end
49
+
50
+ # Returns an array of stripped lines for each line where the passed block returns true
51
+ def select(from: nil, to: nil, &block)
52
+ retval = []
53
+
54
+ each_with_depth(from: from, to: to) do |line, depth, index|
55
+ retval << line if (block_given? == false || block.call(line, depth, index))
56
+ end
57
+
58
+ retval
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,20 @@
1
+ module Effective
2
+ class Resource
3
+ include Effective::Resources::Associations
4
+ include Effective::Resources::Attributes
5
+ include Effective::Resources::Init
6
+ include Effective::Resources::Klass
7
+ include Effective::Resources::Naming
8
+ include Effective::Resources::Paths
9
+
10
+ # post, Post, Admin::Post, admin::Post, admin/posts, admin/post, admin/effective::post
11
+ def initialize(input)
12
+ _initialize(input)
13
+ end
14
+
15
+ def to_s
16
+ name
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,27 @@
1
+ module Effective
2
+ module Resources
3
+ module Associations
4
+
5
+ def belong_tos
6
+ @belong_tos ||= klass.reflect_on_all_associations(:belongs_to)
7
+ end
8
+
9
+ def nested_resources
10
+ @nested ||= klass.reflect_on_all_autosave_associations.inject({}) do |hash, reference|
11
+ hash[reference] = Effective::Resource.new(reference); hash
12
+ end
13
+ end
14
+
15
+ def has_manys
16
+ end
17
+
18
+ def scopes
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+
25
+
26
+
27
+
@@ -0,0 +1,67 @@
1
+ module Effective
2
+ module Resources
3
+ module Attributes
4
+
5
+ def attributes
6
+ (klass_attributes.presence || written_attributes.presence)
7
+ end
8
+
9
+ def attribute_names
10
+ attributes.map { |attribute| attribute.name }
11
+ end
12
+
13
+ # All attributes from the klass, sorted as per attributes block.
14
+ # Does not include :id, :created_at, :updated_at
15
+ def klass_attributes
16
+ attributes = (klass.new().attributes rescue nil)
17
+ return [] unless attributes
18
+
19
+ attributes = (attributes.keys - [klass.primary_key, 'created_at', 'updated_at'] - belong_tos.map { |reference| reference.foreign_key }).map do |att|
20
+ if klass.respond_to?(:column_for_attribute) # Rails 4+
21
+ Effective::Attribute.new(att, klass.column_for_attribute(att).try(:type))
22
+ else
23
+ Effective::Attribute.new(att, klass.columns_hash[att].try(:type))
24
+ end
25
+ end
26
+
27
+ sort(attributes)
28
+ end
29
+
30
+ def belong_tos_attributes
31
+ belong_tos.map do |reference|
32
+ unless reference.foreign_key == 'site_id' && klass.respond_to?(:acts_as_site_specific)
33
+ Effective::Attribute.new(reference.foreign_key, :integer)
34
+ end
35
+ end.compact.sort
36
+ end
37
+
38
+ def written_attributes
39
+ _initialize_written if @written_attributes.nil?
40
+ @written_attributes
41
+ end
42
+
43
+ private
44
+
45
+ def sort(attributes)
46
+ attributes.sort do |a, b|
47
+ index = nil
48
+
49
+ index ||= if written_attributes.include?(a) && written_attributes.include?(b)
50
+ written_attributes.index(a) <=> written_attributes.index(b)
51
+ elsif written_attributes.include?(a) && !written_attributes.include?(b)
52
+ -1
53
+ elsif !written_attributes.include?(a) && written_attributes.include?(b)
54
+ 1
55
+ end
56
+
57
+ index || a <=> b
58
+ end
59
+ end
60
+
61
+ end
62
+ end
63
+ end
64
+
65
+
66
+
67
+
@@ -0,0 +1,50 @@
1
+ module Effective
2
+ module Resources
3
+ module Init
4
+
5
+ private
6
+
7
+ def _initialize(input)
8
+ @input_name = _initialize_input_name(input)
9
+ end
10
+
11
+ def _initialize_input_name(input)
12
+ case input
13
+ when String ; input
14
+ when Symbol ; input
15
+ when Class ; input.name
16
+ when ActiveRecord::Reflection::MacroReflection ; input.name
17
+ when nil ; raise 'expected a string or class'
18
+ else ; input.class.name
19
+ end.to_s.downcase
20
+ end
21
+
22
+ # Lazy initialized
23
+ def _initialize_written
24
+ @written_attributes = []
25
+ @written_belong_tos = []
26
+ @written_scopes = []
27
+
28
+ Effective::CodeReader.new(model_file) do |reader|
29
+ first = reader.index { |line| line == '# Attributes' }
30
+ last = reader.index(from: first) { |line| line.start_with?('#') == false && line.length > 0 } if first
31
+
32
+ if first && last
33
+ @written_attributes = reader.select(from: first+1, to: last-1).map do |line|
34
+ Effective::Attribute.parse(line).presence
35
+ end.compact
36
+ end
37
+
38
+ @written_belong_tos = reader.select { |line| line.start_with?('belongs_to ') }.map do |line|
39
+ line.scan(/belongs_to\s+:(\w+)/).flatten.first
40
+ end
41
+
42
+ @written_scopes = reader.select { |line| line.start_with?('scope ') }.map do |line|
43
+ line.scan(/scope\s+:(\w+)/).flatten.first
44
+ end
45
+ end
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,22 @@
1
+ module Effective
2
+ module Resources
3
+ module Klass
4
+
5
+ def klass
6
+ namespaced_class_name.safe_constantize || class_name.safe_constantize || name.constantize
7
+ end
8
+
9
+ def datatable_klass
10
+ @datatable_klass ||= if defined?(EffectiveDatatables)
11
+ "#{namespaced_class_name.pluralize}Datatable".safe_constantize ||
12
+ "#{class_name.pluralize.camelize}Datatable".safe_constantize ||
13
+ "#{name.pluralize.camelize}Datatable".safe_constantize ||
14
+ "Effective::Datatables::#{namespaced_class_name.pluralize}".safe_constantize ||
15
+ "Effective::Datatables::#{class_name.pluralize.camelize}".safe_constantize ||
16
+ "Effective::Datatables::#{name.pluralize.camelize}".safe_constantize
17
+ end
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,40 @@
1
+ module Effective
2
+ module Resources
3
+ module Naming
4
+ SPLIT = /\/|::/ # / or ::
5
+
6
+ def name # 'post'
7
+ @name ||= @input_name.split(SPLIT).last.singularize
8
+ end
9
+
10
+ def plural_name # 'posts'
11
+ name.pluralize
12
+ end
13
+
14
+ def class_name # 'Effective::Post'
15
+ @class_name ||= (@input_name.split(SPLIT) - namespaces).map { |name| name.classify } * '::'
16
+ end
17
+
18
+ def class_path # 'effective'
19
+ class_name.split('::')[0...-1].map { |name| name.underscore } * '/'
20
+ end
21
+
22
+ def namespaced_class_name # 'Admin::Effective::Post'
23
+ (namespaces.map { |name| name.classify } + [class_name]) * '::'
24
+ end
25
+
26
+ def namespace # 'admin/things'
27
+ @namespace ||= (namespaces.join('/') if namespaces.present?)
28
+ end
29
+
30
+ def namespaces # ['admin', 'things']
31
+ @namespaces ||= @input_name.split('/')[0...-1]
32
+ end
33
+
34
+ def human_name
35
+ class_name.gsub('::', ' ')
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,61 @@
1
+ module Effective
2
+ module Resources
3
+ module Paths
4
+
5
+ # Controller REST helper paths
6
+ def index_path
7
+ [namespace, plural_name, 'path'].compact * '_'
8
+ end
9
+
10
+ def new_path
11
+ ['new', namespace, name, 'path'].compact * '_'
12
+ end
13
+
14
+ def show_path
15
+ [namespace, name, 'path'].compact * '_'
16
+ end
17
+
18
+ def edit_path
19
+ ['edit', namespace, name, 'path'].compact * '_'
20
+ end
21
+
22
+ def action_path(action)
23
+ [action, namespace, name, 'path'].compact * '_'
24
+ end
25
+
26
+ # _helper methods also put in the (@thing)
27
+ alias_method :index_path_helper, :index_path
28
+ alias_method :new_path_helper, :new_path
29
+
30
+ def show_path_helper(at: true)
31
+ show_path + '(' + (at ? '@' : '') + name + ')'
32
+ end
33
+
34
+ def edit_path_helper(at: true)
35
+ edit_path + '(' + (at ? '@' : '') + name + ')'
36
+ end
37
+
38
+ def action_path_helper(action, at: true)
39
+ action_path(action) + '(' + (at ? '@' : '') + name + ')'
40
+ end
41
+
42
+ # Default file paths
43
+ def model_file
44
+ File.join('app/models', class_path.to_s, "#{name}.rb")
45
+ end
46
+
47
+ def controller_file
48
+ File.join('app/controllers', namespace.to_s, "#{plural_name}_controller.rb")
49
+ end
50
+
51
+ def datatable_file
52
+ File.join('app/datatables', namespace.to_s, "#{plural_name}_datatable.rb")
53
+ end
54
+
55
+ def view_file(action = :index, partial: false)
56
+ File.join('app/views', namespace.to_s, (namespace.present? ? '' : class_path), plural_name, "#{'_' if partial}#{action}.html.haml")
57
+ end
58
+
59
+ end
60
+ end
61
+ end
@@ -1,3 +1,3 @@
1
1
  module EffectiveResources
2
- VERSION = '0.1.0'.freeze
2
+ VERSION = '0.1.1'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_resources
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code and Effect
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-10 00:00:00.000000000 Z
11
+ date: 2017-01-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -36,6 +36,15 @@ files:
36
36
  - app/controllers/concerns/effective/crud_controller.rb
37
37
  - app/helpers/effective_resources_helper.rb
38
38
  - app/models/effective/access_denied.rb
39
+ - app/models/effective/attribute.rb
40
+ - app/models/effective/code_reader.rb
41
+ - app/models/effective/resource.rb
42
+ - app/models/effective/resources/associations.rb
43
+ - app/models/effective/resources/attributes.rb
44
+ - app/models/effective/resources/init.rb
45
+ - app/models/effective/resources/klass.rb
46
+ - app/models/effective/resources/naming.rb
47
+ - app/models/effective/resources/paths.rb
39
48
  - config/effective_resources.rb
40
49
  - lib/effective_resources.rb
41
50
  - lib/effective_resources/engine.rb