Skip to content

Commit

Permalink
Support resource-based view lookups. (#2)
Browse files Browse the repository at this point in the history
* update readme to explain goal

* views are proven to work

* 2.3.1 ruby travis

* before_script

* respond_to do format

* something's not right

* progress

* views work now, but lookup of models is now broken

* all tests pass

* rubocop -a
  • Loading branch information
NullVoxPopuli authored Oct 28, 2016
1 parent 66d74f2 commit 24ded1c
Show file tree
Hide file tree
Showing 69 changed files with 381 additions and 105 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ language: ruby
bundler_args: --without guard
rvm:
- "2.3.0"
- "2.3.1"
- ruby-head
before_script: bundle install
script: "bundle exec rspec"
addons:
code_climate:
Expand Down
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# frozen_string_literal: true
source 'https://rubygems.org'

# Specify your gem's dependencies in authorizable.gemspec
Expand Down
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ This gem provides a way to re-structure your app so that like-objects are groupe
app/
├── channels/
├── models/
│ └── data/
│ ├── post.rb
│ ├── comment.rb
│ ├── data/
│ │ ├── post.rb
│ │ └── comment.rb
│ └── graph_data.rb
├── jobs/
├── mailers/
│ └── notification_mailer.rb
Expand All @@ -34,7 +35,10 @@ app/
│ └── serializer.rb
└── comments/
├── controller.rb
└── serializer.rb
├── serializer.rb
└── views/
├── index.html.erb
└── create.html.erb
```

Expand Down
1 change: 1 addition & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
# frozen_string_literal: true
require 'bundler/gem_tasks'
10 changes: 7 additions & 3 deletions lib/rails_module_unification.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# frozen_string_literal: true

require 'active_support'
require 'rails_module_unification/active_support_extensions'

module RailsModuleUnification
extend ActiveSupport::Autoload
require 'rails_module_unification/active_support/dependency_extensions'
require 'rails_module_unification/action_view/path_extensions'
require 'rails_module_unification/action_view/resource_resolver'
require 'rails_module_unification/resource_parts'

module_function

Expand All @@ -17,5 +19,7 @@ def directory
end

require 'rails_module_unification/railtie'
ActiveSupport::Dependencies.extend RailsModuleUnification::ActiveSupportExtensions
ActiveSupport::Dependencies.extend RailsModuleUnification::DependencyExtensions
ActionController::Base.extend RailsModuleUnification::PathExtensions
ActionController::API.extend RailsModuleUnification::PathExtensions
end
43 changes: 43 additions & 0 deletions lib/rails_module_unification/action_view/path_extensions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# frozen_string_literal: true
module RailsModuleUnification
# prepend view paths, setting preferential lookup to the new
# RMU folders
#
# lookup pattern
# resources/:namespace/:resource/views/:action/{.:locale,}{.:formats,}{+:variants,}{.:handlers,}
# prefix = resources/:namespace/:resource/views/
#
# default lookup pattern (for reference (as of 5.0.0.1))
# :prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}
#
# This module should only be used as class methods on the inheriting object
module PathExtensions
require 'awesome_print'

def local_prefixes
[_rmu_resource_path] + super
end

private

def _rmu_resource_path
[
_namespace,
_resource_name,
'views'
].flatten.reject(&:blank?).map(&:underscore).join('/')
end

def _resource_name
controller_name
end

def _namespace
_resource_parts.namespace
end

def _resource_parts
@_resource_parts ||= RailsModuleUnification::ResourceParts.call(name)
end
end
end
44 changes: 44 additions & 0 deletions lib/rails_module_unification/action_view/resource_resolver.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# frozen_string_literal: true
module RailsModuleUnification
class ResourceResolver < ::ActionView::OptimizedFileSystemResolver
require 'pry-byebug'
def initialize
path = [
Rails.root,
'app',
RailsModuleUnification.directory,
'resources'
].reject(&:blank?).join('/')

super(path)
@path = path
end

# def find_templates(name, prefix, partial, details)
# binding.pry
# super(name, prefix, partial, details)
# end
#
# def build_query(path, details)
#
# query = @pattern.dup
#
# binding.pry
# prefix = path.prefix.empty? ? '' : "#{escape_entry(path.prefix)}\\1"
# query.gsub!(/:prefix(\/)?/, prefix)
#
# partial = escape_entry(path.partial? ? "_#{path.name}" : path.name)
# query.gsub!(/:action/, partial)
#
# details.each do |ext, candidates|
# if ext == :variants && candidates == :any
# query.gsub!(/:#{ext}/, "*")
# else
# query.gsub!(/:#{ext}/, "{#{candidates.compact.uniq.join(',')}}")
# end
# end
# puts File.expand_path(query, @path)
# File.expand_path(query, @path)
# end
end
end
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true
module RailsModuleUnification
module ActiveSupportExtensions
module DependencyExtensions
RESOURCE_SUFFIX_NAMES = %w(
Controller
Serializer
Expand Down Expand Up @@ -76,61 +76,11 @@ def load_from_path(file_path, qualified_name, from_mod, const_name)
#
# @param [String] qualified_name fully qualified class/module name to find the file location for
def resource_path_from_qualified_name(qualified_name)
# 1. break apart the qualified name into pieces that can easily be
# manipulated
#
# Api::Posts
# => Api, Posts
#
# Api::PostOperations::Create
# => Api, Post, Operations, Create
#
# Api::PostsController
# => Api, Posts, Controller
#
# Api::V2::PostOperations::Update
# => Api, V2, Post, Operations, Update
qualified_parts = qualified_name.split(QUALIFIED_NAME_SPLIT).reject(&:blank?)

# based on the position of of the resource type name,
# anything to the left will be the namespace, and anything
# to the right will be the file path within the namespace
# (may be obvious, but basically, we're 'pivoting' on RESOURCE_SUFFIX_NAMES)
#
# Given: Api, V2, Post, Operations, Update
# ^ index_of_resource_type (3)
index_of_resource_type = qualified_parts.index { |x| RESOURCE_SUFFIX_NAMES.include?(x) }

# if this is not part of a resource, don't even bother
return unless index_of_resource_type

# Api, V2, Post, Operations, Update
# => Operations
resource_type = qualified_parts[index_of_resource_type]

# Api, V2, Post, Operations, Update
# => Posts
#
# Posts, Controller
# => Posts
original_resource_name = qualified_parts[index_of_resource_type - 1]
resource_name = original_resource_name.pluralize

# Posts_Controller
# Post_Operations
named_resource_type = "#{original_resource_name}_#{resource_type}"

# Api, V2, Post, Operations, Update
# => Api, V2
namespace_index = index_of_resource_type - 1
namespace = namespace_index < 1 ? '' : qualified_parts.take(namespace_index)

# Api, V2, Post, Operations, Update
# => Update
class_index = index_of_resource_type + 1
class_path = class_index < 1 ? '' : qualified_parts.drop(class_index)

# Finally,
namespace,
resource_name,
resource_type, named_resource_type,
class_path = ResourceParts.from_name(qualified_name)

# build all the possible places that this file could be
path_options = [

Expand All @@ -149,7 +99,6 @@ def resource_path_from_qualified_name(qualified_name)

file_path = ''
path_options.each do |path_option|

file_path = search_for_file(path_option)

break if file_path.present?
Expand Down
22 changes: 16 additions & 6 deletions lib/rails_module_unification/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,30 @@ class Railtie < Rails::Railtie
load 'tasks/rails_module_unification.rake'
end

# for customizing where the new folder structure is
# by default, everything still resides in Rails.root/app
config_path = "#{Rails.root}/config/initializers/rails_module_unification"
config_exists = File.exist?(config_path)
require config_path if config_exists

# add folders to autoload paths
initializer 'activeservice.autoload', before: :set_autoload_paths do |app|
mu_dir = "#{Rails.root}/app/#{RailsModuleUnification.directory}"
mu_dir = [
Rails.root,
'app',
RailsModuleUnification.directory
].reject(&:blank?).join('/')

# Data
data_paths = Dir["#{mu_dir}/models/data/**/"]
app.config.autoload_paths += data_paths
# New location for ActiveRecord Models
app.config.autoload_paths << "#{mu_dir}/models/data"

# Resources
resource_paths = Dir["#{mu_dir}/resources/"]
app.config.autoload_paths += resource_paths
app.config.autoload_paths << "#{mu_dir}/resources/"
end

config.after_initialize do
# binding.pry
ActionController::Base.prepend_view_path RailsModuleUnification::ResourceResolver.new
end
end
end
97 changes: 97 additions & 0 deletions lib/rails_module_unification/resource_parts.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# frozen_string_literal: true
module RailsModuleUnification
class ResourceParts
RESOURCE_SUFFIX_NAMES = RailsModuleUnification::DependencyExtensions::RESOURCE_SUFFIX_NAMES
QUALIFIED_NAME_SPLIT = RailsModuleUnification::DependencyExtensions::QUALIFIED_NAME_SPLIT

attr_reader :namespace, :resource_name,
:resource_type, :named_resource_type,
:class_path

class << self
def from_name(name)
resource = call(name)

[
resource.namespace,
resource.resource_name,
resource.resource_type,
resource.named_resource_type,
resource.class_path
]
end

def call(name)
resource = new(name)
resource.call
resource
end
end

def initialize(name)
@qualified_name = name
end

def call
# if this is not part of a resource, don't even bother
return unless index_of_resource_type

# Api, V2, Post, Operations, Update
# => Operations
@resource_type = qualified_parts[index_of_resource_type]

# Api, V2, Post, Operations, Update
# => Posts
#
# Posts, Controller
# => Posts
original_resource_name = qualified_parts[index_of_resource_type - 1]
@resource_name = original_resource_name.pluralize

# Posts_Controller
# Post_Operations
@named_resource_type = "#{original_resource_name}_#{@resource_type}"

# Api, V2, Post, Operations, Update
# => Api, V2
namespace_index = index_of_resource_type - 1
@namespace = namespace_index < 1 ? '' : qualified_parts.take(namespace_index)

# Api, V2, Post, Operations, Update
# => Update
class_index = index_of_resource_type + 1
@class_path = class_index < 1 ? '' : qualified_parts.drop(class_index)
end

private

# 1. break apart the qualified name into pieces that can easily be
# manipulated
#
# Api::Posts
# => Api, Posts
#
# Api::PostOperations::Create
# => Api, Post, Operations, Create
#
# Api::PostsController
# => Api, Posts, Controller
#
# Api::V2::PostOperations::Update
# => Api, V2, Post, Operations, Update
def qualified_parts
@qualified_parts ||= @qualified_name.split(QUALIFIED_NAME_SPLIT).reject(&:blank?)
end

# based on the position of of the resource type name,
# anything to the left will be the namespace, and anything
# to the right will be the file path within the namespace
# (may be obvious, but basically, we're 'pivoting' on RESOURCE_SUFFIX_NAMES)
#
# Given: Api, V2, Post, Operations, Update
# ^ index_of_resource_type (3)
def index_of_resource_type
@index_of_resource_type ||= qualified_parts.index { |x| RESOURCE_SUFFIX_NAMES.include?(x) }
end
end
end
2 changes: 1 addition & 1 deletion lib/rails_module_unification/version.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# frozen_string_literal: true
module RailsModuleUnification
VERSION = '0.6.1'.freeze
VERSION = '0.6.1'
end
Loading

0 comments on commit 24ded1c

Please sign in to comment.