effective_resources 0.1.0 → 0.1.1

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 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