react_on_rails 1.1.1 → 1.2.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +1 -0
- data/.travis.yml +2 -8
- data/Dockerfile_tests +1 -1
- data/Gemfile +68 -2
- data/README.md +21 -15
- data/Rakefile +3 -109
- data/app/assets/javascripts/react_on_rails.js +10 -0
- data/docker-compose.yml +0 -6
- data/docs/additional_reading/webpack.md +46 -0
- data/docs/generator_testing.md +20 -0
- data/lib/generators/react_on_rails/base_generator.rb +49 -9
- data/lib/generators/react_on_rails/bootstrap_generator.rb +14 -29
- data/lib/generators/react_on_rails/dev_tests_generator.rb +30 -0
- data/lib/generators/react_on_rails/install_generator.rb +14 -3
- data/lib/generators/react_on_rails/templates/base/base/app/views/hello_world/index.html.erb.tt +2 -3
- data/lib/generators/react_on_rails/templates/base/base/client/package.json.tt +26 -23
- data/lib/generators/react_on_rails/templates/base/base/client/{webpack.client.hot.config.js → webpack.client.hot.config.js.tt} +3 -1
- data/lib/generators/react_on_rails/templates/base/base/lib/tasks/{assets.rake → assets.rake.tt} +2 -0
- data/lib/generators/react_on_rails/templates/base/server_rendering/client/webpack.server.rails.config.js +1 -1
- data/lib/generators/react_on_rails/templates/dev_tests/.rspec +2 -0
- data/lib/generators/react_on_rails/templates/dev_tests/spec/features/hello_world_spec.rb +25 -0
- data/lib/generators/react_on_rails/templates/dev_tests/spec/rails_helper.rb +57 -0
- data/lib/generators/react_on_rails/templates/dev_tests/spec/simplecov_helper.rb +21 -0
- data/lib/generators/react_on_rails/templates/dev_tests/spec/spec_helper.rb +95 -0
- data/lib/react_on_rails/version.rb +1 -1
- data/rakelib/docker.rake +33 -0
- data/rakelib/dummy_apps.rake +29 -0
- data/rakelib/example_type.rb +160 -0
- data/rakelib/examples.rake +103 -0
- data/rakelib/examples_config.yml +19 -0
- data/rakelib/lint.rake +37 -0
- data/rakelib/run_rspec.rake +65 -0
- data/rakelib/task_helpers.rb +44 -0
- data/ruby-lint.yml +1 -0
- metadata +22 -9
- data/Dockerfile_ci +0 -12
- data/docs/generator_testing_script.md +0 -49
- data/lib/generators/react_on_rails/templates/client/README.md +0 -97
@@ -0,0 +1,21 @@
|
|
1
|
+
# Starts SimpleCov for code coverage.
|
2
|
+
|
3
|
+
if ENV["COVERAGE"]
|
4
|
+
require "simplecov"
|
5
|
+
|
6
|
+
# Using a command name prevents results from getting clobbered by other test suites
|
7
|
+
example_name = File.basename(File.expand_path("../../../.", __FILE__))
|
8
|
+
SimpleCov.command_name(example_name)
|
9
|
+
|
10
|
+
SimpleCov.start("rails") do
|
11
|
+
# Consider the entire gem project as the root
|
12
|
+
# (typically this will be the folder named "react_on_rails")
|
13
|
+
gem_root_path = File.expand_path("../../../../.", __FILE__)
|
14
|
+
root gem_root_path
|
15
|
+
|
16
|
+
# Don't report anything that has "spec" in the path
|
17
|
+
add_filter do |src|
|
18
|
+
src.filename =~ %r{\/spec\/}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# This file was generated by the `rails generate rspec:install` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# The generated `.rspec` file contains `--require spec_helper` which will cause
|
4
|
+
# this file to always be loaded, without a need to explicitly require it in any
|
5
|
+
# files.
|
6
|
+
#
|
7
|
+
# Given that it is always loaded, you are encouraged to keep this file as
|
8
|
+
# light-weight as possible. Requiring heavyweight dependencies from this file
|
9
|
+
# will add to the boot time of your test suite on EVERY test run, even for an
|
10
|
+
# individual file that may not need all of that loaded. Instead, consider making
|
11
|
+
# a separate helper file that requires the additional dependencies and performs
|
12
|
+
# the additional setup, and require it from the spec files that actually need
|
13
|
+
# it.
|
14
|
+
#
|
15
|
+
# The `.rspec` file also contains a few flags that are not defaults but that
|
16
|
+
# users commonly want.
|
17
|
+
#
|
18
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
19
|
+
RSpec.configure do |config|
|
20
|
+
# rspec-expectations config goes here. You can use an alternate
|
21
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
22
|
+
# assertions if you prefer.
|
23
|
+
config.expect_with :rspec do |expectations|
|
24
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
25
|
+
# and `failure_message` of custom matchers include text for helper methods
|
26
|
+
# defined using `chain`, e.g.:
|
27
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
28
|
+
# # => "be bigger than 2 and smaller than 4"
|
29
|
+
# ...rather than:
|
30
|
+
# # => "be bigger than 2"
|
31
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
32
|
+
end
|
33
|
+
|
34
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
35
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
36
|
+
config.mock_with :rspec do |mocks|
|
37
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
38
|
+
# a real object. This is generally recommended, and will default to
|
39
|
+
# `true` in RSpec 4.
|
40
|
+
mocks.verify_partial_doubles = true
|
41
|
+
end
|
42
|
+
|
43
|
+
# The settings below are suggested to provide a good initial experience
|
44
|
+
# with RSpec, but feel free to customize to your heart's content.
|
45
|
+
# rubocop:disable Style/BlockComments
|
46
|
+
=begin
|
47
|
+
|
48
|
+
# These two settings work together to allow you to limit a spec run
|
49
|
+
# to individual examples or groups you care about by tagging them with
|
50
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
51
|
+
# get run.
|
52
|
+
config.filter_run :focus
|
53
|
+
config.run_all_when_everything_filtered = true
|
54
|
+
|
55
|
+
# Allows RSpec to persist some state between runs in order to support
|
56
|
+
# the `--only-failures` and `--next-failure` CLI options. We recommend
|
57
|
+
# you configure your source control system to ignore this file.
|
58
|
+
config.example_status_persistence_file_path = "spec/examples.txt"
|
59
|
+
|
60
|
+
# Limits the available syntax to the non-monkey patched syntax that is
|
61
|
+
# recommended. For more details, see:
|
62
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
63
|
+
# - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
64
|
+
# - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
|
65
|
+
config.disable_monkey_patching!
|
66
|
+
|
67
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
68
|
+
# file, and it's useful to allow more verbose output when running an
|
69
|
+
# individual spec file.
|
70
|
+
if config.files_to_run.one?
|
71
|
+
# Use the documentation formatter for detailed output,
|
72
|
+
# unless a formatter has already been configured
|
73
|
+
# (e.g. via a command-line flag).
|
74
|
+
config.default_formatter = 'doc'
|
75
|
+
end
|
76
|
+
|
77
|
+
# Print the 10 slowest examples and example groups at the
|
78
|
+
# end of the spec run, to help surface which specs are running
|
79
|
+
# particularly slow.
|
80
|
+
config.profile_examples = 10
|
81
|
+
|
82
|
+
# Run specs in random order to surface order dependencies. If you find an
|
83
|
+
# order dependency and want to debug it, you can fix the order by providing
|
84
|
+
# the seed, which is printed after each run.
|
85
|
+
# --seed 1234
|
86
|
+
config.order = :random
|
87
|
+
|
88
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
89
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
90
|
+
# test failures related to randomization by passing the same `--seed` value
|
91
|
+
# as the one that triggered the failure.
|
92
|
+
Kernel.srand config.seed
|
93
|
+
=end
|
94
|
+
end
|
95
|
+
# rubocop:enable Style/BlockComments
|
data/rakelib/docker.rake
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
namespace :docker do
|
2
|
+
desc "Run Rubocop linter from docker"
|
3
|
+
task :rubocop do
|
4
|
+
sh "docker-compose run lint rake lint:rubocop"
|
5
|
+
end
|
6
|
+
|
7
|
+
desc "Run ruby-lint linter from docker"
|
8
|
+
task :ruby do
|
9
|
+
sh "docker-compose run lint rake lint:ruby"
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "Run scss-lint linter from docker"
|
13
|
+
task :scss do
|
14
|
+
sh "docker-compose run lint rake lint:scss"
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Run eslint linter from docker"
|
18
|
+
task :eslint do
|
19
|
+
sh "docker-compose run lint rake lint:eslint"
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "Run jscs linter from docker"
|
23
|
+
task :jscs do
|
24
|
+
sh "docker-compose run lint rake lint:jscs"
|
25
|
+
end
|
26
|
+
desc "Run all linting from docker"
|
27
|
+
task :lint do
|
28
|
+
sh "docker-compose run lint rake lint"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "Runs all linters from docker. Run `rake -D docker` to see all available lint options"
|
33
|
+
task docker: ["docker:lint"]
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative "task_helpers"
|
2
|
+
include ReactOnRails::TaskHelpers
|
3
|
+
|
4
|
+
namespace :dummy_apps do
|
5
|
+
task :dummy_app do
|
6
|
+
dummy_app_dir = File.join(gem_root, "spec/dummy")
|
7
|
+
bundle_install_in(dummy_app_dir)
|
8
|
+
dummy_app_client_dir = File.join(dummy_app_dir, "client")
|
9
|
+
sh_in_dir(dummy_app_client_dir, ["npm install",
|
10
|
+
"$(npm bin)/webpack --config webpack.server.js",
|
11
|
+
"$(npm bin)/webpack --config webpack.client.js"])
|
12
|
+
end
|
13
|
+
|
14
|
+
task :dummy_react_013_app do
|
15
|
+
dummy_app_dir = File.join(gem_root, "spec/dummy-react-013")
|
16
|
+
bundle_install_in(dummy_app_dir)
|
17
|
+
dummy_app_client_dir = File.join(dummy_app_dir, "client")
|
18
|
+
sh_in_dir(dummy_app_client_dir, ["npm install",
|
19
|
+
"$(npm bin)/webpack --config webpack.server.js",
|
20
|
+
"$(npm bin)/webpack --config webpack.client.js"])
|
21
|
+
end
|
22
|
+
|
23
|
+
task dummy_apps: [:dummy_app, :dummy_react_013_app] do
|
24
|
+
puts "Prepared all Dummy Apps"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "Prepares all dummy apps by installing dependencies"
|
29
|
+
task dummy_apps: ["dummy_apps:dummy_apps"]
|
@@ -0,0 +1,160 @@
|
|
1
|
+
require "rake"
|
2
|
+
require "pathname"
|
3
|
+
|
4
|
+
require_relative "task_helpers"
|
5
|
+
|
6
|
+
# Defines the ExampleType class, where each object represents a unique type of example
|
7
|
+
# app that we can generate.
|
8
|
+
module ReactOnRails
|
9
|
+
module TaskHelpers
|
10
|
+
class ExampleType
|
11
|
+
def self.all
|
12
|
+
@example_types ||= []
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.namespace_name
|
16
|
+
"examples"
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :name, :generator_options
|
20
|
+
|
21
|
+
def initialize(name:, generator_options:)
|
22
|
+
@name = name
|
23
|
+
@generator_options = generator_options
|
24
|
+
self.class.all << self
|
25
|
+
end
|
26
|
+
|
27
|
+
def name_pretty
|
28
|
+
"#{@name} example app"
|
29
|
+
end
|
30
|
+
|
31
|
+
def server_rendering?
|
32
|
+
generator_options.include?("--server-rendering")
|
33
|
+
end
|
34
|
+
|
35
|
+
def dir
|
36
|
+
File.join(examples_dir, name)
|
37
|
+
end
|
38
|
+
|
39
|
+
def dir_exist?
|
40
|
+
Dir.exist?(dir)
|
41
|
+
end
|
42
|
+
|
43
|
+
def client_dir
|
44
|
+
File.join(dir, "client")
|
45
|
+
end
|
46
|
+
|
47
|
+
def source_package_json
|
48
|
+
File.join(gem_root, "lib/generators/react_on_rails/templates/base/base/client/package.json.tt")
|
49
|
+
end
|
50
|
+
|
51
|
+
def node_modules_dir
|
52
|
+
File.join(client_dir, "node_modules")
|
53
|
+
end
|
54
|
+
|
55
|
+
def webpack_bundles_dir
|
56
|
+
File.join(dir, "app", "assets", "javascripts", "generated")
|
57
|
+
end
|
58
|
+
|
59
|
+
def webpack_bundles
|
60
|
+
bundles = []
|
61
|
+
bundles << File.join(webpack_bundles_dir, "app-bundle.js")
|
62
|
+
bundles << File.join(webpack_bundles_dir, "server-bundle.js") if server_rendering?
|
63
|
+
bundles << File.join(webpack_bundles_dir, "vendor-bundle.js")
|
64
|
+
end
|
65
|
+
|
66
|
+
def gemfile
|
67
|
+
File.join(dir, "Gemfile")
|
68
|
+
end
|
69
|
+
|
70
|
+
def gemfile_lock
|
71
|
+
"#{gemfile}.lock"
|
72
|
+
end
|
73
|
+
|
74
|
+
def package_json
|
75
|
+
File.join(client_dir, "package.json")
|
76
|
+
end
|
77
|
+
|
78
|
+
# Gems we need to add to the Gemfile before bundle installing
|
79
|
+
def required_gems
|
80
|
+
relative_gem_root = Pathname(gem_root).relative_path_from(Pathname(dir))
|
81
|
+
["gem 'react_on_rails', path: '#{relative_gem_root}'"]
|
82
|
+
end
|
83
|
+
|
84
|
+
# Options we pass when running `rails new` from the command-line
|
85
|
+
def rails_options
|
86
|
+
"--skip-bundle --skip-spring --skip-git --skip-test-unit --skip-active-record"
|
87
|
+
end
|
88
|
+
|
89
|
+
# Methods for retrieving the name of a task specific to the example type
|
90
|
+
%w(gen prepare clean clobber npm_install build_webpack_bundles).each do |task_type|
|
91
|
+
method = "#{task_type}_task_name" # ex: `clean_task_name`
|
92
|
+
task_name = "#{task_type}_#{name}" # ex: `clean_basic`
|
93
|
+
|
94
|
+
define_method(method) { "#{self.class.namespace_name}:#{task_name}" }
|
95
|
+
define_method("#{method}_short") { task_name }
|
96
|
+
end
|
97
|
+
|
98
|
+
def rspec_task_name_short
|
99
|
+
"example_#{name}"
|
100
|
+
end
|
101
|
+
|
102
|
+
def rspec_task_name
|
103
|
+
"run_rspec:#{rspec_task_name_short}"
|
104
|
+
end
|
105
|
+
|
106
|
+
def source_files
|
107
|
+
FileList.new(all_files_in_dir(generators_source_dir))
|
108
|
+
end
|
109
|
+
|
110
|
+
# Note: we need to explicitly declare a file we know is supposed to be there
|
111
|
+
# to indicate that the example is in need of being rebuilt in the case of its absence.
|
112
|
+
def generated_files
|
113
|
+
FileList.new(all_files_in_dir(dir)) do |fl|
|
114
|
+
fl.include(gemfile) # explicitly declared file (dependency of Gemfile.lock)
|
115
|
+
fl.include(package_json) # explicitly declared file (dependency of NPM Install)
|
116
|
+
fl.exclude(%r{client(/node_modules(.+)?)?$}) # leave node_modules folder
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def generated_client_files
|
121
|
+
generated_files.exclude { |f| !f.start_with?(client_dir) }
|
122
|
+
end
|
123
|
+
|
124
|
+
# generated files plus explicitly included files resulting from running
|
125
|
+
# bundle install, npm install, and generating the webpack bundles
|
126
|
+
def prepared_files
|
127
|
+
generated_files
|
128
|
+
.include(webpack_bundles)
|
129
|
+
.include(node_modules_dir)
|
130
|
+
.include(gemfile_lock)
|
131
|
+
end
|
132
|
+
|
133
|
+
def clean_files
|
134
|
+
generated_files
|
135
|
+
end
|
136
|
+
|
137
|
+
# Assumes we are inside client folder
|
138
|
+
def build_webpack_bundles_shell_commands
|
139
|
+
webpack_command = File.join("$(npm bin)", "webpack")
|
140
|
+
shell_commands = []
|
141
|
+
shell_commands << "#{webpack_command} --config webpack.server.rails.config.js" if server_rendering?
|
142
|
+
shell_commands << "#{webpack_command} --config webpack.client.rails.config.js"
|
143
|
+
end
|
144
|
+
|
145
|
+
# Assumes we are inside a rails app's folder and necessary gems have been installed
|
146
|
+
def generator_shell_commands
|
147
|
+
shell_commands = []
|
148
|
+
shell_commands << "rails generate react_on_rails:install #{generator_options}"
|
149
|
+
shell_commands << "rails generate react_on_rails:dev_tests #{generator_options}"
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
# Defines globs that scoop up all files (including dotfiles) in given directory
|
155
|
+
def all_files_in_dir(p_dir)
|
156
|
+
[File.join(p_dir, "**", "*"), File.join(p_dir, "**", ".*")]
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# Defines tasks related to generating example apps using the gem's generator.
|
2
|
+
# Allows us to create and test apps generated using a wide range of options.
|
3
|
+
#
|
4
|
+
# Also see example_type.rb
|
5
|
+
|
6
|
+
require "yaml"
|
7
|
+
require_relative "example_type"
|
8
|
+
require_relative "task_helpers"
|
9
|
+
include ReactOnRails::TaskHelpers
|
10
|
+
|
11
|
+
namespace :examples do
|
12
|
+
# Loads data from examples_config.yml and instantiates corresponding ExampleType objects
|
13
|
+
examples_config_file = File.expand_path("../examples_config.yml", __FILE__)
|
14
|
+
examples_config = symbolize_keys(YAML.load(File.read(examples_config_file)))
|
15
|
+
examples_config[:example_type_data].each { |example_type_data| ExampleType.new(symbolize_keys(example_type_data)) }
|
16
|
+
|
17
|
+
# Define tasks for each example type
|
18
|
+
ExampleType.all.each do |example_type|
|
19
|
+
# GENERATED FILES
|
20
|
+
example_type.generated_files.each do |f|
|
21
|
+
file f => example_type.source_files do
|
22
|
+
Rake::Task[example_type.gen_task_name].invoke
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# GEMFILE.LOCK
|
27
|
+
file example_type.gemfile_lock => example_type.gemfile do
|
28
|
+
bundle_install_in(example_type.dir)
|
29
|
+
end
|
30
|
+
|
31
|
+
# WEBPACK BUNDLES
|
32
|
+
example_type.webpack_bundles.each do |f|
|
33
|
+
file f => example_type.generated_client_files do
|
34
|
+
Rake::Task[example_type.build_webpack_bundles_task_name].invoke
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# BUILD WEBPACK BUNDLES
|
39
|
+
task example_type.build_webpack_bundles_task_name_short => example_type.npm_install_task_name do
|
40
|
+
sh_in_dir(example_type.client_dir, example_type.build_webpack_bundles_shell_commands)
|
41
|
+
end
|
42
|
+
|
43
|
+
# NPM INSTALL
|
44
|
+
task example_type.npm_install_task_name_short => example_type.package_json do
|
45
|
+
unless uptodate?(example_type.node_modules_dir, [example_type.source_package_json])
|
46
|
+
sh_in_dir(example_type.client_dir, "npm install")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# CLEAN
|
51
|
+
desc "Cleans #{example_type.name_pretty}"
|
52
|
+
task example_type.clean_task_name_short do
|
53
|
+
example_type.clean_files.each { |f| rm_rf(f) }
|
54
|
+
end
|
55
|
+
|
56
|
+
# CLOBBER
|
57
|
+
desc "Clobbers (deletes) #{example_type.name_pretty}"
|
58
|
+
task example_type.clobber_task_name_short do
|
59
|
+
rm_rf(example_type.dir)
|
60
|
+
end
|
61
|
+
|
62
|
+
# GENERATE
|
63
|
+
desc "Generates #{example_type.name_pretty}"
|
64
|
+
task example_type.gen_task_name_short => example_type.clean_task_name do
|
65
|
+
mkdir_p(example_type.dir)
|
66
|
+
sh_in_dir(examples_dir, "rails new #{example_type.name} #{example_type.rails_options}")
|
67
|
+
append_to_gemfile(example_type.gemfile, example_type.required_gems)
|
68
|
+
bundle_install_in(example_type.dir)
|
69
|
+
sh_in_dir(example_type.dir, example_type.generator_shell_commands)
|
70
|
+
end
|
71
|
+
|
72
|
+
# PREPARE
|
73
|
+
desc "Prepares #{example_type.name_pretty} (generates example, `npm install`s, and generates webpack bundles)"
|
74
|
+
multitask example_type.prepare_task_name_short => example_type.prepared_files
|
75
|
+
end
|
76
|
+
|
77
|
+
desc "Cleans all example apps"
|
78
|
+
multitask clean: ExampleType.all.map(&:clean_task_name)
|
79
|
+
|
80
|
+
desc "Clobbers (deletes) all example apps"
|
81
|
+
task :clobber do
|
82
|
+
rm_rf(examples_dir)
|
83
|
+
end
|
84
|
+
|
85
|
+
desc "Generates all example apps"
|
86
|
+
multitask gen_all: ExampleType.all.map(&:gen_task_name)
|
87
|
+
|
88
|
+
desc "Prepares all example apps"
|
89
|
+
multitask prepare_all: ExampleType.all.map(&:prepare_task_name)
|
90
|
+
end
|
91
|
+
|
92
|
+
desc "Prepares all example apps. Run `rake -D examples` to see all available options"
|
93
|
+
multitask examples: ["examples:prepare_all"]
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
# Appends each string in an array as a new line of text in the given Gemfile.
|
98
|
+
# Automatically adds line returns.
|
99
|
+
def append_to_gemfile(gemfile, lines)
|
100
|
+
old_text = File.read(gemfile)
|
101
|
+
new_text = lines.reduce(old_text) { |a, e| a << "#{e}\n" }
|
102
|
+
File.open(gemfile, "w") { |f| f.puts(new_text) }
|
103
|
+
end
|