rails_drivers 0.1.0 → 0.4.0

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
  SHA256:
3
- metadata.gz: '03862c5a464a55793074ecb863ac4e7eb4530172923a5f0a8207d8051bf54f96'
4
- data.tar.gz: ffc80ffc2db7d8f71f5598f388fa3cce4efc8399362aa841d3b21cc776508df3
3
+ metadata.gz: b0d06c45078456d085945c12374a78a6e8cd8c5ae83ad7abc7fd55cda175e8c4
4
+ data.tar.gz: 18d5755774da8c851c0a39a25bcfa061445ac1acc06a98e27b22acb896443ab2
5
5
  SHA512:
6
- metadata.gz: 2a89e522b5a3fac4be2323be01f8d9e2085e2b54ceee263b1835b75bbae7df2397d68b57c690bcf11f7df1ca5efce2be9add211bcd7a247a8d96e3d3dc1bf7e0
7
- data.tar.gz: df27e76af55099c0031644db8a156124ffa2b72d9ef67ef6a4427119f3c35497979814348bf86cf8ec4a20065834d6dc3839f27647f248eccee2cf2c16812fff
6
+ metadata.gz: '09235347fa19b0da2ea6f20bebdfc4f2048c51760df3889575d7d6cd376a658ccac01a1053b03feaa7f6f89a329ca60db323aa2eb365b62977e29bd05e20a20c'
7
+ data.tar.gz: cff5ba12b81d820189e145f8b15535ec8ab42942963cb31eb501c359f4f2561171326d1cf93adab12df6d1a540f2f2465f4cb18c304316049561237409005656
data/README.md CHANGED
@@ -4,20 +4,20 @@
4
4
 
5
5
  Each driver is like a mini Rails app that has full access to the main app. A driver has its own `app`, `config`, `spec`, and `db` folder.
6
6
 
7
- Technically speaking, drivers are just a way to put code into a different folder, but there are some rules we like to follow in order to reduce application complexity:
7
+ Technically speaking, drivers are just a fancy name for putting code into a different folder. The advantage of doing this is that it provides clear-cut separation of concerns. If we follow a couple of simple rules, we can actually test that separation:
8
8
 
9
9
  - Drivers should not touch other drivers
10
10
  - The main app should not touch drivers directly
11
11
 
12
12
  The "main app" refers to the files inside your `<project root>/app` directory.
13
13
 
14
- Thankfully, we can test that these rules are adhered to by removing drivers before running the test suite.
14
+ If your test suite is good, you can test that these rules are adhered to by selectively adding and removing drivers before running your tests.
15
15
 
16
16
  ## Aren't these just engines?
17
17
 
18
18
  Very similar, yes. They use the same Rails facilities for adding new `app` paths, etc.
19
19
 
20
- But Drivers have one useful property: they can be freely added and removed from your project without breaking anything.
20
+ But Drivers have less friction. They can be freely added and removed from your project without breaking anything. There's no need to mess around with gems, vendoring, or dummy apps.
21
21
 
22
22
  ## Usage
23
23
 
@@ -34,6 +34,21 @@ Just run `rails g driver my_new_driver_name`.
34
34
  The `driver` utility technically works with other generators and rake tasks, but is only guaranteed to work with migrations.
35
35
  The reason is that some generators assume a particular path, rather than using the Rails path methods.
36
36
 
37
+ ### Creating a rake task in a driver
38
+
39
+ Every driver includes a `lib/tasks` directory where you can define rake tasks. Rake tasks defined in drivers are automatically loaded and namespaced.
40
+ For example,
41
+
42
+ ```ruby
43
+ # drivers/my_driver/lib/tasks/my_namespace.rake
44
+ namespace :my_namespace do
45
+ task :task_name do
46
+ end
47
+ end
48
+ ```
49
+
50
+ Can be executed using `rake driver:my_driver:my_namespace:task_name`.
51
+
37
52
  ### Testing for coupling
38
53
 
39
54
  Since drivers are merged into your main application just like engines, there's nothing stopping them from accessing other drivers, and there's nothing stopping your main application from accessing drivers. In order to ensure those things don't happen, we have a handful of rake tasks:
@@ -54,6 +69,11 @@ rake driver:restore
54
69
  rake driver:isolate[admin]
55
70
  rspec --pattern '{spec,drivers/*/spec}/**{,/*/**}/*_spec.rb'
56
71
  rake driver:restore
72
+
73
+ # Short-hand with 'driver' utility!
74
+ bundle exec driver admin do rspec --pattern '{spec,drivers/*/spec}/**{,/*/**}/*_spec.rb'
75
+ # (can run with no drivers as well)
76
+ bundle exec nodriver do rspec --pattern '{spec,drivers/*/spec}/**{,/*/**}/*_spec.rb'
57
77
  ```
58
78
 
59
79
  This lets you to ensure that the store and admin function properly without each other. Note we're running all of the main app's specs twice. This is good because we also want to make sure the main app is not reaching into drivers.
@@ -72,23 +92,69 @@ And then execute:
72
92
  $ bundle install
73
93
  ```
74
94
 
75
- Add this line to your routes.rb
95
+ Add this line to your routes.rb:
76
96
 
77
97
  ```ruby
78
98
  require 'rails_drivers/routes'
99
+
100
+ # This can go before or after your application's route definitions
101
+ RailsDrivers::Routes.load_driver_routes
79
102
  ```
80
103
 
81
- (Optional) Add these lines to your `spec/rails_helper.rb`
104
+ ### RSpec
105
+
106
+ If you use RSpec with FactoryBot, add these lines to your `spec/rails_helper.rb` or `spec/spec_helper.rb`:
82
107
 
83
108
  ```ruby
84
109
  Dir[Rails.root.join("drivers/*/spec/support/*.rb")].each { |f| require f }
85
110
 
86
111
  RSpec.configure do |config|
112
+ FactoryBot.definition_file_paths += Dir['drivers/*/spec/factories']
113
+ FactoryBot.reload
114
+
87
115
  Dir[Rails.root.join('drivers/*/spec')].each { |x| config.project_source_dirs << x }
88
116
  Dir[Rails.root.join('drivers/*/lib')].each { |x| config.project_source_dirs << x }
89
117
  Dir[Rails.root.join('drivers/*/app')].each { |x| config.project_source_dirs << x }
90
118
  end
91
119
  ```
92
120
 
121
+ ### Webpacker
122
+
123
+ If you use Webpacker, take a look at this snippet. You'll want to add the code between the comments:
124
+
125
+ ```javascript
126
+ // config/webpack/environment.js
127
+ const { environment } = require('@rails/webpacker')
128
+
129
+ //// Begin driver code ////
130
+ const { config } = require('@rails/webpacker')
131
+ const { sync } = require('glob')
132
+ const { basename, dirname, join, relative, resolve } = require('path')
133
+ const extname = require('path-complete-extname')
134
+
135
+ const getExtensionsGlob = () => {
136
+ const { extensions } = config
137
+ return extensions.length === 1 ? `**/*${extensions[0]}` : `**/*{${extensions.join(',')}}`
138
+ }
139
+
140
+ const addToEntryObject = (sourcePath) => {
141
+ const glob = getExtensionsGlob()
142
+ const rootPath = join(sourcePath, config.source_entry_path)
143
+ const paths = sync(join(rootPath, glob))
144
+ paths.forEach((path) => {
145
+ const namespace = relative(join(rootPath), dirname(path))
146
+ const name = join(namespace, basename(path, extname(path)))
147
+ environment.entry.set(name, resolve(path))
148
+ })
149
+ }
150
+
151
+ sync('drivers/*').forEach((driverPath) => {
152
+ addToEntryObject(join(driverPath, config.source_path));
153
+ })
154
+ //// End driver code ////
155
+
156
+ module.exports = environment
157
+ ```
158
+
93
159
  ## License
94
160
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/bin/driver CHANGED
@@ -1,15 +1,39 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- APP_PATH = File.expand_path('config/application')
5
- require_relative "#{Dir.pwd}/config/boot"
4
+ require 'rails_drivers/files'
6
5
 
7
- REPLACE_DEFAULT_PATH_WITH_DRIVER = ARGV.shift
6
+ selected_driver = ARGV.shift
8
7
 
9
- possible_drivers = Dir['drivers/*'].map { |d| d.split('/').last }
10
- unless possible_drivers.include?(REPLACE_DEFAULT_PATH_WITH_DRIVER)
11
- puts "Unknown driver #{REPLACE_DEFAULT_PATH_WITH_DRIVER}. Must be one of [#{possible_drivers.join(', ')}]"
12
- exit 1
13
- end
8
+ if ARGV[0] == 'do'
9
+ #
10
+ # Run any command with only one driver present
11
+ #
12
+ ARGV.shift
13
+ at_exit { RailsDrivers::Files.restore }
14
+
15
+ if selected_driver == '_clear'
16
+ RailsDrivers::Files.clear
17
+ else
18
+ RailsDrivers::Files.isolate selected_driver
19
+ end
20
+
21
+ Process.wait Process.spawn(*ARGV)
22
+ exit Process.last_status.exitstatus
23
+ else
24
+ #
25
+ # Run 'rails' command as if the driver was the rails app.
26
+ #
27
+ APP_PATH = File.expand_path('config/application')
28
+ REPLACE_DEFAULT_PATH_WITH_DRIVER = selected_driver
14
29
 
15
- require 'rails/commands'
30
+ require_relative "#{Dir.pwd}/config/boot"
31
+
32
+ possible_drivers = Dir['drivers/*'].map { |d| d.split('/').last }
33
+ unless possible_drivers.include?(REPLACE_DEFAULT_PATH_WITH_DRIVER)
34
+ puts "Unknown driver #{REPLACE_DEFAULT_PATH_WITH_DRIVER}. Must be one of [#{possible_drivers.join(', ')}]"
35
+ exit 1
36
+ end
37
+
38
+ require 'rails/commands'
39
+ end
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'rails_drivers/files'
5
+
6
+ ARGV.shift if ARGV[0] == 'do'
7
+
8
+ at_exit { RailsDrivers::Files.restore }
9
+ RailsDrivers::Files.clear
10
+ Process.wait Process.spawn(*ARGV)
11
+ exit Process.last_status.exitstatus
@@ -9,7 +9,7 @@ class DriverGenerator < Rails::Generators::NamedBase
9
9
  create_file "drivers/#{file_name}/app/views/#{file_name}/.keep", ''
10
10
  create_file "drivers/#{file_name}/spec/.keep", ''
11
11
  create_file "drivers/#{file_name}/db/migrate/.keep", ''
12
- create_file "drivers/#{file_name}/lib/.keep", ''
12
+ create_file "drivers/#{file_name}/lib/tasks/.keep", ''
13
13
 
14
14
  template 'routes.rb.erb', "drivers/#{file_name}/config/routes.rb"
15
15
  template 'initializer.rb.erb', "drivers/#{file_name}/config/initializers/#{file_name}_feature.rb"
@@ -5,11 +5,13 @@ require 'rails_drivers/setup'
5
5
  require 'rails_drivers/railtie'
6
6
 
7
7
  module RailsDrivers
8
- def self.loaded
9
- @loaded ||= []
10
- end
8
+ class << self
9
+ def loaded
10
+ @loaded ||= []
11
+ end
11
12
 
12
- def self.freeze!
13
- @loaded = @loaded&.freeze
13
+ def freeze!
14
+ @loaded = @loaded&.freeze
15
+ end
14
16
  end
15
17
  end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsDrivers
4
+ module Files
5
+ class Error < StandardError
6
+ end
7
+
8
+ module_function
9
+
10
+ def isolate(driver)
11
+ raise Error, 'No driver specified' if driver.nil? || driver == ''
12
+ raise Error, "Driver #{driver.inspect} not found" unless File.exist?("drivers/#{driver}")
13
+
14
+ FileUtils.mkdir_p 'tmp/drivers'
15
+ Dir['drivers/*'].each do |driver_path|
16
+ next if driver_path.include?("/#{driver}")
17
+
18
+ FileUtils.mv driver_path, "tmp/#{driver_path}"
19
+ end
20
+ end
21
+
22
+ def clear
23
+ FileUtils.mkdir_p 'tmp/drivers'
24
+ Dir['drivers/*'].each do |driver_path|
25
+ FileUtils.mv driver_path, "tmp/#{driver_path}"
26
+ end
27
+ end
28
+
29
+ def restore
30
+ Dir['tmp/drivers/*'].each do |tmp_driver_path|
31
+ driver = tmp_driver_path.split('/').last
32
+ FileUtils.mv tmp_driver_path, "drivers/#{driver}"
33
+ end
34
+ end
35
+ end
36
+ end
@@ -6,6 +6,17 @@ module RailsDrivers
6
6
 
7
7
  rake_tasks do
8
8
  load File.expand_path("#{__dir__}/../tasks/rails_drivers_tasks.rake")
9
+
10
+ # load drivers rake tasks
11
+ Dir['drivers/*/lib/tasks/**/*.rake'].each do |driver_rake_file|
12
+ %r{^drivers/(?<driver_name>\w+)/} =~ driver_rake_file
13
+
14
+ namespace(:driver) do
15
+ namespace(driver_name) do
16
+ load driver_rake_file
17
+ end
18
+ end
19
+ end
9
20
  end
10
21
 
11
22
  config.before_configuration { setup_paths }
@@ -11,6 +11,3 @@ module RailsDrivers
11
11
  end
12
12
  end
13
13
  end
14
-
15
- # This is meant to be executed as soon as the file is required
16
- RailsDrivers::Routes.load_driver_routes
@@ -4,6 +4,7 @@ module RailsDrivers
4
4
  module Setup
5
5
  DRIVER_PATHS = %w[
6
6
  app
7
+ app/assets
7
8
  app/models
8
9
  app/views
9
10
  app/controllers
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsDrivers
4
- VERSION = '0.1.0'
4
+ VERSION = '0.4.0'
5
5
  end
@@ -1,46 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  namespace :driver do
4
- DriverError = Class.new(StandardError)
5
-
6
4
  desc 'Removes every driver but the one specified. Can be undone with driver:restore.'
7
5
  task :isolate, [:driver] do |_t, args|
8
- include FileUtils
9
-
10
- raise DriverError, 'No driver specified' if args.driver.blank?
11
- raise DriverError, 'Driver not found' unless File.exist?("drivers/#{args.driver}")
12
-
13
- mkdir_p 'tmp/drivers'
14
- Dir['drivers/*'].each do |driver_path|
15
- next if driver_path.include?("/#{args.driver}")
16
-
17
- mv driver_path, "tmp/#{driver_path}"
18
- puts "Moved #{driver_path} to tmp/drivers/"
19
- end
20
-
21
- rescue DriverError => e
6
+ require 'rails_drivers/files'
7
+ RailsDrivers::Files.isolate(args.driver)
8
+ rescue RailsDrivers::Files::Error => e
22
9
  puts e.message
23
10
  end
24
11
 
25
12
  desc 'Removes all drivers. Can be undone with driver:restore.'
26
13
  task :clear do
27
- include FileUtils
28
-
29
- mkdir_p 'tmp/drivers'
30
- Dir['drivers/*'].each do |driver_path|
31
- mv driver_path, "tmp/#{driver_path}"
32
- puts "Moved #{driver_path} to tmp/drivers/"
33
- end
14
+ require 'rails_drivers/files'
15
+ RailsDrivers::Files.clear
34
16
  end
35
17
 
36
18
  desc 'Undoes the effects of driver:isolate and driver:clear.'
37
19
  task :restore do
38
- include FileUtils
39
-
40
- Dir['tmp/drivers/*'].each do |tmp_driver_path|
41
- driver = tmp_driver_path.split('/').last
42
- mv tmp_driver_path, "drivers/#{driver}"
43
- puts "Moved #{tmp_driver_path} to drivers/"
44
- end
20
+ require 'rails_drivers/files'
21
+ RailsDrivers::Files.restore
45
22
  end
46
23
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_drivers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nigel Baillie
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-12 00:00:00.000000000 Z
11
+ date: 2020-01-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec-rails
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'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: sqlite3
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -52,12 +66,27 @@ dependencies:
52
66
  - - ">="
53
67
  - !ruby/object:Gem::Version
54
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: webpacker
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.5'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.5'
55
83
  description: Like Rails Engines, but without the friction. Your Rails app can't access
56
84
  them, and they can't access each other.
57
85
  email:
58
86
  - nbaillie@degica.com
59
87
  executables:
60
88
  - driver
89
+ - nodriver
61
90
  extensions: []
62
91
  extra_rdoc_files: []
63
92
  files:
@@ -65,12 +94,14 @@ files:
65
94
  - README.md
66
95
  - Rakefile
67
96
  - bin/driver
97
+ - bin/nodriver
68
98
  - lib/generators/driver/USAGE
69
99
  - lib/generators/driver/driver_generator.rb
70
100
  - lib/generators/driver/templates/initializer.rb.erb
71
101
  - lib/generators/driver/templates/module.rb.erb
72
102
  - lib/generators/driver/templates/routes.rb.erb
73
103
  - lib/rails_drivers.rb
104
+ - lib/rails_drivers/files.rb
74
105
  - lib/rails_drivers/railtie.rb
75
106
  - lib/rails_drivers/routes.rb
76
107
  - lib/rails_drivers/setup.rb
@@ -95,7 +126,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
95
126
  - !ruby/object:Gem::Version
96
127
  version: '0'
97
128
  requirements: []
98
- rubygems_version: 3.0.6
129
+ rubygems_version: 3.0.3
99
130
  signing_key:
100
131
  specification_version: 4
101
132
  summary: De-coupled separation of concerns for Rails