drawers 0.9.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f965ef04831e18fcc88dbafe26068c72ac7e40cf
4
+ data.tar.gz: dea8bf6cdd778e52b2c89872cbbf1788ae3da39e
5
+ SHA512:
6
+ metadata.gz: 11d4e29fdb9b5d62addbacc152bf8750c001e616a295a561e46a6067375fc7155a0a3ce76f66991c46255a128140f3d5ac7633d835980735b241d171df83653c
7
+ data.tar.gz: 94322be4dcd79e7285c20398228dc367edf8dcd26e9122681abb25e34525708533da8c56c51f8ef90fa3d9a576020b7b98f99dfc094654509ce074c8004642aa
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2016 L. Preston Sego III
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,129 @@
1
+ # drawers
2
+ Group like-classes together. No more silos.
3
+
4
+ [![Gem Version](https://badge.fury.io/rb/drawers.svg)](https://badge.fury.io/rb/drawers)
5
+ [![Build Status](https://travis-ci.org/NullVoxPopuli/drawers.svg?branch=master)](https://travis-ci.org/NullVoxPopuli/drawers)
6
+ [![Code Climate](https://codeclimate.com/repos/57dddb2c50dac40e6900197c/badges/73a0a0761e417c655b68/gpa.svg)](https://codeclimate.com/repos/57dddb2c50dac40e6900197c/feed)
7
+ [![Test Coverage](https://codeclimate.com/repos/57dddb2c50dac40e6900197c/badges/73a0a0761e417c655b68/coverage.svg)](https://codeclimate.com/repos/57dddb2c50dac40e6900197c/coverage)
8
+ [![Dependency Status](https://gemnasium.com/badges/github.com/NullVoxPopuli/drawers.svg)](https://gemnasium.com/github.com/NullVoxPopuli/drawers)
9
+
10
+
11
+ ## What is this about?
12
+
13
+ With large rails application, the default architecture can result in a resource's related files being very spread out through the overall project structure. For example, lets say you have 50 controllers, serializers, policies, and operations. That's _four_ different top level folders that spread out all the related objects. It makes sense do it this way, as it makes rails' autoloading programmatically easy.
14
+
15
+ This gem provides a way to re-structure your app so that like-objects are grouped together.
16
+
17
+ All this gem does is add some new autoloading / path resolution logic. This gem does not provide any service/operation/policy/etc functionality.
18
+
19
+ **All of this is optional, and can be slowly migrated to over time. Adding this gem does not force you to change your app.**
20
+
21
+ ### The new structure
22
+
23
+ ```ruby
24
+ app/
25
+ ├── channels/
26
+ ├── models/
27
+ │ ├── data/
28
+ │ │ ├── post.rb
29
+ │ │ └── comment.rb
30
+ │ └── graph_data.rb
31
+ ├── jobs/
32
+ ├── mailers/
33
+ │ └── notification_mailer.rb
34
+ └── resources/
35
+ ├── posts/
36
+ │ ├── controller.rb # or posts_controller.rb
37
+ │ ├── operations.rb # or post_operations.rb
38
+ │ ├── policy.rb # or post_policy.rb
39
+ │ └── serializer.rb # or post_serializer.rb
40
+ └── comments/
41
+ ├── controller.rb
42
+ ├── serializer.rb
43
+ └── views/
44
+ ├── index.html.erb
45
+ └── create.html.erb
46
+
47
+ ```
48
+
49
+ Does this new structure mean you have to change the class names of all your classes? Nope. In the above example file structure, `app/resources/posts/controller.rb` _still_ defines `class PostsController < ApplicationController`
50
+
51
+ [Checkout the sample rails app in the tests directory.](https://github.com/NullVoxPopuli/drawers/tree/master/spec/support/rails_app/app)
52
+
53
+ ### The Convention
54
+
55
+ Say, for example, you have _any_ class/module defined as:
56
+
57
+ ```ruby
58
+ module Api # {namespace
59
+ module V3 # namespace}
60
+ module UserServices # {resource_name}{resource_type}
61
+ module Authentication # {class_path
62
+ class OAuth2 # class_path/file_name}
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ ```
69
+
70
+ As long as some part of the fully qualified class name (in this example: `Api::V3::UserServices::Authentication::OAuth2`) contains any of the [defined keywords](https://github.com/NullVoxPopuli/drawers/blob/master/lib/drawers/active_support/dependency_extensions.rb#L4), the file will be found at `app/resources/api/v3/users/services/authentication/oauth2.rb`.
71
+
72
+ The pattern for this is: `app/resources/:namespace/:resource_name/:resource_type/:class_path` where:
73
+ - `:namespace` is the namespace/parents of the `UserService`
74
+ - `:resource_type` is a suffix that may be inferred by checking of the inclusion of the defined keywords (linked above)
75
+ - `:resource_name` is the same module/class as what the `resource_type` is derived from, sans the `resource_type`
76
+ - `:class_path` is the remaining namespaces and eventually the class that the target file defines.
77
+
78
+ So... what if you have a set of classes that don't fit the pattern exactly? You can leave those files where they are currently, or move them to `app/resources`, if it makes sense to do so. Feel free to open an issue / PR if you feel the list of resource types needs updating.
79
+
80
+ ## Usage
81
+
82
+ ```ruby
83
+ gem 'drawers'
84
+ ```
85
+
86
+ Including the gem in your gemfile enables the new structure.
87
+
88
+ ### A note for ActiveModelSerializers
89
+
90
+ ActiveModelSerializers, be default, does not consider your _controller's_ namespace when searching for searializers.
91
+
92
+ To address that problem, you'll need to add this to the serializer lookup chain
93
+
94
+ ```ruby
95
+ # config/initializers/active_model_serializers.rb
96
+ ActiveModelSerializers.config.serializer_lookup_chain.unshift(
97
+ lambda do |resource_class, _, namespace|
98
+ "#{namespace.name}::#{resource_class.name}Serializer" if namespace
99
+ end
100
+ )
101
+ ```
102
+ Note: as of 2016-11-04, only [this branch of AMS](https://github.com/rails-api/active_model_serializers/pull/1757) supports a confnigurable lookup chain
103
+
104
+ ## Migrating
105
+
106
+ Each part of your app can be migrated gradually (either manually or automatically).
107
+
108
+ In order to automatically migrate resources, just run:
109
+
110
+ ```bash
111
+ rake rmu:migrate_resource[Post]
112
+ ```
113
+
114
+ This will move all unnamespaced classes that contain any of the [supported resource suffixes](https://github.com/NullVoxPopuli/drawers/blob/master/lib/drawers/active_support_extensions.rb#L4) to the `app/resources/posts` directory.
115
+
116
+ ## Configuration
117
+
118
+ ```ruby
119
+ # (Rails.root)/config/initializers/drawers.rb
120
+ Drawers.directory = 'pods'
121
+ ```
122
+
123
+ Sets the folder for the new structure to be in the `app/pods` directory if you want the new structure separate from the main app files.
124
+
125
+ ## Contributing
126
+
127
+ Feel free to open an issue, or fork and make a pull request.
128
+
129
+ All discussion is welcome :-)
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ module Drawers
6
+ require 'drawers/active_support/dependency_extensions'
7
+ require 'drawers/action_view/path_extensions'
8
+ require 'drawers/action_view/resource_resolver'
9
+ require 'drawers/resource_parts'
10
+
11
+ module_function
12
+
13
+ def directory=(dir)
14
+ @directory = dir
15
+ end
16
+
17
+ def directory
18
+ @directory || ''
19
+ end
20
+
21
+ require 'drawers/railtie'
22
+ ActiveSupport::Dependencies.extend Drawers::DependencyExtensions
23
+ ActionController::Base.extend Drawers::PathExtensions
24
+
25
+ if Rails.version > '5'
26
+ ActionController::API.extend Drawers::PathExtensions
27
+ end
28
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+ module Drawers
3
+ # prepend view paths, setting preferential lookup to the new
4
+ # RMU folders
5
+ #
6
+ # lookup pattern
7
+ # resources/:namespace/:resource/views/:action/{.:locale,}{.:formats,}{+:variants,}{.:handlers,}
8
+ # prefix = resources/:namespace/:resource/views/
9
+ #
10
+ # default lookup pattern (for reference (as of 5.0.0.1))
11
+ # :prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}
12
+ #
13
+ # This module should only be used as class methods on the inheriting object
14
+ module PathExtensions
15
+ def local_prefixes
16
+ [_rmu_resource_path] + super
17
+ end
18
+
19
+ private
20
+
21
+ def _rmu_resource_path
22
+ [
23
+ _namespace,
24
+ _resource_name,
25
+ 'views'
26
+ ].flatten.reject(&:blank?).map(&:underscore).join('/')
27
+ end
28
+
29
+ def _resource_name
30
+ controller_name
31
+ end
32
+
33
+ def _namespace
34
+ _resource_parts.namespace
35
+ end
36
+
37
+ def _resource_parts
38
+ @_resource_parts ||= Drawers::ResourceParts.call(name)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'action_view'
4
+
5
+ module Drawers
6
+ class ResourceResolver < ::ActionView::OptimizedFileSystemResolver
7
+ def initialize
8
+ path = [
9
+ Rails.root,
10
+ 'app',
11
+ Drawers.directory,
12
+ 'resources'
13
+ ].reject(&:blank?).join('/')
14
+
15
+ super(path)
16
+ @path = path
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+ module Drawers
3
+ module DependencyExtensions
4
+ RESOURCE_SUFFIX_NAMES = %w(
5
+ Controller
6
+ Serializer
7
+ Operations
8
+ Presenters
9
+ Policy
10
+ Policies
11
+ Services
12
+ ).freeze
13
+
14
+ # Join all the suffix names together with an "OR" operator
15
+ RESOURCE_SUFFIXES = /(#{RESOURCE_SUFFIX_NAMES.join('|')})/
16
+
17
+ # split on any of the resource suffixes OR the ruby namespace seperator
18
+ QUALIFIED_NAME_SPLIT = /::|#{RESOURCE_SUFFIXES}/
19
+
20
+ def load_from_path(file_path, qualified_name, from_mod, const_name)
21
+ expanded = File.expand_path(file_path)
22
+ expanded.sub!(/\.rb\z/, '')
23
+
24
+ if loading.include?(expanded)
25
+ raise "Circular dependency detected while autoloading constant #{qualified_name}"
26
+ else
27
+ require_or_load(expanded, qualified_name)
28
+ unless from_mod.const_defined?(const_name, false)
29
+ raise LoadError, "Unable to autoload constant #{qualified_name}, expected #{file_path} to define it"
30
+ end
31
+
32
+ return from_mod.const_get(const_name)
33
+ end
34
+ end
35
+
36
+ # A look for the possible places that various qualified names could be
37
+ #
38
+ # @note The Lookup Rules:
39
+ # - all resources are plural
40
+ # - file_names can either be named after the type or traditional ruby/rails nameing
41
+ # i.e.: posts_controller.rb vs controller.rb
42
+ # - regular namespacing still applies.
43
+ # i.e: Api::V2::CategoriesController should be in
44
+ # api/v2/categories/controller.rb
45
+ #
46
+ # @note The Pattern:
47
+ # - namespace_a - api
48
+ # - namespace_b - v2
49
+ # - resource_name (plural) - posts
50
+ # - file_type.rb - controller.rb (or posts_controller.rb)
51
+ # - operations.rb (or post_operations.rb)
52
+ # - folder_type - operations/ (or post_operations/)
53
+ # - related namespaced classes - create.rb
54
+ #
55
+ # All examples assume default resource directory ("resources")
56
+ # and show the order of lookup
57
+ #
58
+ # @example Api::PostsController
59
+ # Possible Locations
60
+ # - api/posts/controller.rb
61
+ # - api/posts/posts_controller.rb
62
+ #
63
+ # @example Api::PostSerializer
64
+ # Possible Locations
65
+ # - api/posts/serializer.rb
66
+ # - api/posts/post_serializer.rb
67
+ #
68
+ # @example Api::PostOperations::Create
69
+ # Possible Locations
70
+ # - api/posts/operations/create.rb
71
+ # - api/posts/post_operations/create.rb
72
+ #
73
+ # @example Api::V2::CategoriesController
74
+ # Possible Locations
75
+ # - api/v2/categories/controller.rb
76
+ # - api/v2/categories/categories_controller.rb
77
+ #
78
+ # @param [String] qualified_name fully qualified class/module name to find the file location for
79
+ def resource_path_from_qualified_name(qualified_name)
80
+ namespace,
81
+ resource_name,
82
+ resource_type, named_resource_type,
83
+ class_path = ResourceParts.from_name(qualified_name)
84
+
85
+ # build all the possible places that this file could be
86
+ path_options = [
87
+
88
+ # api/v2/posts/operations/update
89
+ to_path(namespace, resource_name, resource_type, class_path),
90
+
91
+ # api/v2/posts/post_operations/update
92
+ to_path(namespace, resource_name, named_resource_type, class_path),
93
+
94
+ # api/v2/posts/posts_controller
95
+ to_path(namespace, resource_name, named_resource_type),
96
+
97
+ # api/v2/posts/controller
98
+ to_path(namespace, resource_name, resource_type)
99
+ ].uniq
100
+
101
+ file_path = ''
102
+ path_options.each do |path_option|
103
+ file_path = search_for_file(path_option)
104
+
105
+ break if file_path.present?
106
+ end
107
+
108
+ file_path
109
+ end
110
+
111
+ def to_path(*args)
112
+ args.flatten.reject(&:blank?).map(&:underscore).join('/')
113
+ end
114
+
115
+ # Load the constant named +const_name+ which is missing from +from_mod+. If
116
+ # it is not possible to load the constant into from_mod, try its parent
117
+ # module using +const_missing+.
118
+ def load_missing_constant(from_mod, const_name)
119
+ # always default to the actual implementation
120
+ super
121
+ rescue LoadError, NameError => e
122
+ load_missing_constant_error(from_mod, const_name, e)
123
+ end
124
+
125
+ # the heavy lifting of Drawers is just
126
+ # adding some additional pathfinding / constat lookup logic
127
+ # when the default (super) can't find what needs to be found
128
+ #
129
+ # @param [Class] from_mod - parent module / class that const_name may be a part of
130
+ # @param [Symbol] const_name - potential constant to lookup under from_mod
131
+ # @param [Exception] e - exception from previous error
132
+ def load_missing_constant_error(from_mod, const_name, e)
133
+ # examples
134
+ # - Api::PostsController
135
+ # - PostsController
136
+ qualified_name = qualified_name_for(from_mod, const_name)
137
+ file_path = resource_path_from_qualified_name(qualified_name)
138
+
139
+ begin
140
+ return load_from_path(file_path, qualified_name, from_mod, const_name) if file_path
141
+ rescue LoadError, NameError => e
142
+ # Recurse!
143
+ # not found, check the parent
144
+ at_the_top = from_mod.parent == from_mod
145
+ return load_missing_constant_error(from_mod.parent, const_name, e) unless at_the_top
146
+ raise e
147
+ end
148
+
149
+ name_error = NameError.new(e.message)
150
+ name_error.set_backtrace(caller.reject { |l| l.starts_with? __FILE__ })
151
+ raise name_error
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+ require 'rails/railtie'
3
+ require 'action_controller'
4
+
5
+ module Drawers
6
+ class Railtie < Rails::Railtie
7
+ railtie_name :drawers
8
+
9
+ rake_tasks do
10
+ load 'tasks/drawers.rake'
11
+ end
12
+
13
+ # for customizing where the new folder structure is
14
+ # by default, everything still resides in Rails.root/app
15
+ config_path = "#{Rails.root}/config/initializers/drawers"
16
+ config_exists = File.exist?(config_path)
17
+ require config_path if config_exists
18
+
19
+ # add folders to autoload paths
20
+ initializer 'activeservice.autoload', before: :set_autoload_paths do |app|
21
+ mu_dir = [
22
+ Rails.root,
23
+ 'app',
24
+ Drawers.directory
25
+ ].reject(&:blank?).join('/')
26
+
27
+ # New location for ActiveRecord Models
28
+ app.config.autoload_paths << "#{mu_dir}/models/data"
29
+
30
+ # Resources
31
+ app.config.autoload_paths << "#{mu_dir}/resources/"
32
+ end
33
+
34
+ config.after_initialize do
35
+ ActionController::Base.prepend_view_path Drawers::ResourceResolver.new
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+ module Drawers
3
+ class ResourceParts
4
+ RESOURCE_SUFFIX_NAMES = Drawers::DependencyExtensions::RESOURCE_SUFFIX_NAMES
5
+ QUALIFIED_NAME_SPLIT = Drawers::DependencyExtensions::QUALIFIED_NAME_SPLIT
6
+
7
+ attr_reader :namespace, :resource_name,
8
+ :resource_type, :named_resource_type,
9
+ :class_path
10
+
11
+ class << self
12
+ def from_name(name)
13
+ resource = call(name)
14
+
15
+ [
16
+ resource.namespace,
17
+ resource.resource_name,
18
+ resource.resource_type,
19
+ resource.named_resource_type,
20
+ resource.class_path
21
+ ]
22
+ end
23
+
24
+ def call(name)
25
+ resource = new(name)
26
+ resource.call
27
+ resource
28
+ end
29
+ end
30
+
31
+ def initialize(name)
32
+ @qualified_name = name
33
+ end
34
+
35
+ def call
36
+ # if this is not part of a resource, don't even bother
37
+ return unless index_of_resource_type
38
+
39
+ # Api, V2, Post, Operations, Update
40
+ # => Operations
41
+ @resource_type = qualified_parts[index_of_resource_type]
42
+
43
+ # Api, V2, Post, Operations, Update
44
+ # => Posts
45
+ #
46
+ # Posts, Controller
47
+ # => Posts
48
+ original_resource_name = qualified_parts[index_of_resource_type - 1]
49
+ @resource_name = original_resource_name.pluralize
50
+
51
+ # Posts_Controller
52
+ # Post_Operations
53
+ @named_resource_type = "#{original_resource_name}_#{@resource_type}"
54
+
55
+ # Api, V2, Post, Operations, Update
56
+ # => Api, V2
57
+ namespace_index = index_of_resource_type - 1
58
+ @namespace = namespace_index < 1 ? '' : qualified_parts.take(namespace_index)
59
+
60
+ # Api, V2, Post, Operations, Update
61
+ # => Update
62
+ class_index = index_of_resource_type + 1
63
+ @class_path = class_index < 1 ? '' : qualified_parts.drop(class_index)
64
+ end
65
+
66
+ private
67
+
68
+ # 1. break apart the qualified name into pieces that can easily be
69
+ # manipulated
70
+ #
71
+ # Api::Posts
72
+ # => Api, Posts
73
+ #
74
+ # Api::PostOperations::Create
75
+ # => Api, Post, Operations, Create
76
+ #
77
+ # Api::PostsController
78
+ # => Api, Posts, Controller
79
+ #
80
+ # Api::V2::PostOperations::Update
81
+ # => Api, V2, Post, Operations, Update
82
+ def qualified_parts
83
+ @qualified_parts ||= @qualified_name.split(QUALIFIED_NAME_SPLIT).reject(&:blank?)
84
+ end
85
+
86
+ # based on the position of of the resource type name,
87
+ # anything to the left will be the namespace, and anything
88
+ # to the right will be the file path within the namespace
89
+ # (may be obvious, but basically, we're 'pivoting' on RESOURCE_SUFFIX_NAMES)
90
+ #
91
+ # Given: Api, V2, Post, Operations, Update
92
+ # ^ index_of_resource_type (3)
93
+ def index_of_resource_type
94
+ @index_of_resource_type ||= qualified_parts.index { |x| RESOURCE_SUFFIX_NAMES.include?(x) }
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ module Drawers
3
+ VERSION = '0.9.0'
4
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+ namespace :rmu do
3
+ config_path = "#{Rails.root}/config/initializers/drawers"
4
+ config_exists = File.exist?(config_path)
5
+ require config_path if config_exists
6
+
7
+ # @example
8
+ # rake rmu:migrate_resource Post
9
+ # @example
10
+ # rake rmu:migrate_resource Api::Event
11
+
12
+ desc 'Moves all files related to a resource to the RMU directory'
13
+ task :migrate_resource, [:klass_name] => :environment do |_t, args|
14
+ klass_name = args[:klass_name]
15
+
16
+ # Given klass_name,
17
+ # check known places where related files could be
18
+ singular = possible_classes(klass_name)
19
+ plural = possible_classes(klass_name, plural: true)
20
+
21
+ possibilities = singular + plural
22
+
23
+ possibilities.each do |possible_class|
24
+ klass = possible_class.safe_constantize
25
+
26
+ next unless klass
27
+
28
+ location = location_of(klass)
29
+
30
+ unless location
31
+ puts "#{klass.name} could not be found"
32
+ next
33
+ end
34
+
35
+ next if already_moved?(location)
36
+
37
+ destination = destination_for(location)
38
+ move_file(location, to: destination)
39
+ end
40
+ end
41
+
42
+ def destination_for(path)
43
+ # Rails.root does not include 'app/'
44
+ project_root = Rails.root.to_s
45
+ relative_path = path.sub(project_root, '')
46
+ relative_path_parts = relative_path.split('/').reject(&:blank?)
47
+
48
+ # remove app dir
49
+ # ["app", "serializers", "hosted_event_serializer.rb"]
50
+ # => ["serializers", "hosted_event_serializer.rb"]
51
+ relative_path_parts.shift
52
+
53
+ return if relative_path_parts.length < 2
54
+
55
+ # resource type (controller, serializer, etc)
56
+ # ["serializers", "hosted_event_serializer.rb"]
57
+ # => serializer
58
+ resource_type = relative_path_parts.shift.singularize
59
+
60
+ # ["hosted_event_serializer.rb"]
61
+ # => hosted_event_serializer.rb
62
+ file_name = relative_path_parts.pop
63
+
64
+ resource_name, extension = file_name.split("_#{resource_type}")
65
+
66
+ namespace = relative_path_parts.join('/')
67
+
68
+ destination = [
69
+ project_root,
70
+ 'app',
71
+ Drawers.directory,
72
+ 'resources',
73
+ namespace,
74
+ resource_name.pluralize,
75
+ resource_type
76
+ ].reject(&:blank?).join('/')
77
+
78
+ destination + extension
79
+ end
80
+
81
+ def move_file(from, to: nil)
82
+ puts "Moving #{from} to #{to}"
83
+ return unless to && from
84
+
85
+ matches = /.+\/(.+\.\w+)/.match(to)
86
+ file = matches[1]
87
+ path = to.sub(file, '')
88
+
89
+ unless File.directory?(path)
90
+ puts 'creating directory...'
91
+ FileUtils.mkdir_p(path)
92
+ end
93
+
94
+ FileUtils.move(from, to)
95
+ end
96
+
97
+ # See RESOURCE_SUFFIX_NAMES for total options
98
+ # PostSerializer
99
+ SINGULAR_RESOURCE_SUFFIXES = %w(
100
+ Serializer
101
+ Operations
102
+ Presenters
103
+ Policy
104
+ Policies
105
+ ).freeze
106
+
107
+ # PostsController
108
+ PLURAL_RESOURCE_SUFFIXES = %w(
109
+ Controller
110
+ ).freeze
111
+
112
+ def possible_classes(resource_name, plural: false)
113
+ klass_name = plural ? resource_name.pluralize : resource_name
114
+ suffixes = plural ? PLURAL_RESOURCE_SUFFIXES : SINGULAR_RESOURCE_SUFFIXES
115
+ suffixes.map { |suffix| "#{klass_name}#{suffix}" }
116
+ end
117
+
118
+ def location_of(klass)
119
+ # Flat default rails structure
120
+ guessed_path = guess_file_path(klass)
121
+ return guessed_path if File.exist?(guessed_path)
122
+
123
+ # Try to find the file based on method source location
124
+ # - will not work if a file doesn't define any methods
125
+ # ( empty sub classes )
126
+ root = Rails.root.to_s
127
+ possible_paths = klass.instance_methods(false).map do |m|
128
+ klass.instance_method(m).source_location.first
129
+ end.uniq
130
+
131
+ possible_paths.select { |path| path.include?(root) }.first
132
+ end
133
+
134
+ def already_moved?(location)
135
+ location.include?("#{Drawers.directory}/resources")
136
+ end
137
+
138
+ def guess_file_path(klass)
139
+ underscored = klass.name.underscore
140
+ file_name = underscored + '.rb'
141
+ resource_type = underscored.split('_').last
142
+
143
+ guessed_path = File.join(
144
+ Rails.root,
145
+ 'app',
146
+ resource_type.pluralize,
147
+ file_name
148
+ )
149
+
150
+ guessed_path
151
+ end
152
+ end
metadata ADDED
@@ -0,0 +1,179 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: drawers
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ platform: ruby
6
+ authors:
7
+ - L. Preston Sego III
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-12-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rubocop
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: awesome_print
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry-byebug
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: factory_girl
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: factory_girl_rails
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec-rails
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description: Group like-classes together. No more silos.
140
+ email: LPSego3+dev@gmail.com
141
+ executables: []
142
+ extensions: []
143
+ extra_rdoc_files: []
144
+ files:
145
+ - LICENSE
146
+ - README.md
147
+ - lib/drawers.rb
148
+ - lib/drawers/action_view/path_extensions.rb
149
+ - lib/drawers/action_view/resource_resolver.rb
150
+ - lib/drawers/active_support/dependency_extensions.rb
151
+ - lib/drawers/railtie.rb
152
+ - lib/drawers/resource_parts.rb
153
+ - lib/drawers/version.rb
154
+ - lib/tasks/drawers.rake
155
+ homepage: https://github.com/NullVoxPopuli/drawers
156
+ licenses:
157
+ - MIT
158
+ metadata: {}
159
+ post_install_message:
160
+ rdoc_options: []
161
+ require_paths:
162
+ - lib
163
+ required_ruby_version: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ version: '2.0'
168
+ required_rubygems_version: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ requirements: []
174
+ rubyforge_project:
175
+ rubygems_version: 2.5.1
176
+ signing_key:
177
+ specification_version: 4
178
+ summary: Drawers-0.9.0
179
+ test_files: []