sugar_utils 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +15 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +11 -0
- data/README.md +10 -7
- data/Rakefile +2 -1
- data/lib/sugar_utils/file.rb +61 -23
- data/lib/sugar_utils/version.rb +1 -1
- data/spec/spec_helper.rb +43 -2
- data/spec/sugar_utils/file_spec.rb +101 -66
- data/sugar_utils.gemspec +9 -5
- metadata +64 -51
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4b841240e219afa9f5e780a7c3bd9ae389916c48
|
4
|
+
data.tar.gz: fbb71eb94aaac991e157b215d3eb38c4af935fe2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fe6d9af436894005aed3882bc59387d5bb240a50780d32c1e0de9cc1443d8bbfac9ec146076a9a15d5d49d5abf80e736220f506de2ec30ed2db6a74a930fdf81
|
7
|
+
data.tar.gz: eaf877c2a627817cc6f89837c51a35c89641abb6346633c6c74759a94242001c6dcc9021d5aca0a984111791d0527a610949d985192a1979edbd239ffbca6b31
|
data/.rubocop.yml
CHANGED
@@ -1,2 +1,17 @@
|
|
1
1
|
Documentation:
|
2
2
|
Enabled: false
|
3
|
+
|
4
|
+
Metrics/LineLength:
|
5
|
+
Max: 90
|
6
|
+
|
7
|
+
Metrics/MethodLength:
|
8
|
+
Max: 20
|
9
|
+
|
10
|
+
Metrics/AbcSize:
|
11
|
+
Max: 19
|
12
|
+
|
13
|
+
# Because of the way that blocks are used in RSpecs can end up being long when
|
14
|
+
# example groups are nested or many examples are checked.
|
15
|
+
Metrics/BlockLength:
|
16
|
+
Exclude:
|
17
|
+
- 'spec/**/*'
|
data/.travis.yml
CHANGED
@@ -3,8 +3,15 @@ rvm:
|
|
3
3
|
- 2.0
|
4
4
|
- 2.1
|
5
5
|
- 2.2
|
6
|
+
- 2.3.3
|
7
|
+
- 2.4.0
|
6
8
|
- ruby-head
|
7
9
|
matrix:
|
8
10
|
allow_failures:
|
9
11
|
- rvm: ruby-head
|
10
12
|
sudo: false
|
13
|
+
addons:
|
14
|
+
code_climate:
|
15
|
+
repo_token: c250bc1551dd13a07efdd5bc8a594924f6f762f7be9762e79da0c8f065cf93dd
|
16
|
+
after_success:
|
17
|
+
- bundle exec codeclimate-test-reporter
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,17 @@
|
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
3
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
4
4
|
|
5
|
+
## [0.3.0] - 2017-03-29
|
6
|
+
### Added
|
7
|
+
- SugarUtils::File.read, with locking and error handling when reading a plain
|
8
|
+
text file
|
9
|
+
- SugarUtils::File.write, with locking and error handling when writing a plain
|
10
|
+
text file
|
11
|
+
|
12
|
+
### Changed
|
13
|
+
- explicitly specify the Ruby v2.0.0 support limit
|
14
|
+
- divide SugarUtils::File.flock into .flock_shared and .flock_exclusive
|
15
|
+
|
5
16
|
## [0.2.0] - 2016-07-21
|
6
17
|
### Added
|
7
18
|
- SugarUtils::File.flock, for file locking with a timeout
|
data/README.md
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
[](http://badge.fury.io/rb/sugar_utils)
|
4
4
|
[](https://gemnasium.com/sugarcrm/sugar_utils)
|
5
5
|
[](https://travis-ci.org/sugarcrm/sugar_utils)
|
6
|
-
[](https://coveralls.io/r/sugarcrm/sugar_utils)
|
7
6
|
[](https://codeclimate.com/github/sugarcrm/sugar_utils)
|
7
|
+
[](https://codeclimate.com/github/sugarcrm/sugar_utils/coverage)
|
8
8
|
[](http://inch-ci.org/github/sugarcrm/sugar_utils)
|
9
9
|
[](LICENSE)
|
10
10
|
|
@@ -12,16 +12,20 @@ Utility methods extracted from SugarCRM Ruby projects.
|
|
12
12
|
|
13
13
|
These methods are included:
|
14
14
|
|
15
|
-
* ensure_boolean
|
16
|
-
* ensure_integer
|
15
|
+
* SugarUtils.ensure_boolean
|
16
|
+
* SugarUtils.ensure_integer
|
17
|
+
* SugarUtils::File.flock_shared
|
18
|
+
* SugarUtils::File.flock_exclusive
|
19
|
+
* SugarUtils::File.read
|
20
|
+
* SugarUtils::File.write
|
21
|
+
* SugarUtils::File.read_json
|
22
|
+
* SugarUtils::File.write_json
|
17
23
|
|
18
24
|
These methods will probably be included in the future:
|
19
25
|
|
20
26
|
* sizeof_dir
|
21
27
|
* find_files
|
22
28
|
* find_file!
|
23
|
-
* read_json
|
24
|
-
* write_json
|
25
29
|
* gzip
|
26
30
|
* gunzip
|
27
31
|
* tarball
|
@@ -30,7 +34,6 @@ These methods will probably be included in the future:
|
|
30
34
|
* encrypt
|
31
35
|
* http_get_file
|
32
36
|
* timeout_retry
|
33
|
-
* flock_with_timeout
|
34
37
|
|
35
38
|
## Installation
|
36
39
|
|
@@ -44,7 +47,7 @@ gem 'sugar_utils'
|
|
44
47
|
And then execute:
|
45
48
|
|
46
49
|
```bash
|
47
|
-
$ bundle
|
50
|
+
$ bundle install
|
48
51
|
```
|
49
52
|
|
50
53
|
Or install it yourself as:
|
data/Rakefile
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
+
|
2
3
|
require 'bundler/gem_tasks'
|
3
4
|
require 'rspec/core/rake_task'
|
4
5
|
require 'rubocop/rake_task'
|
@@ -22,6 +23,6 @@ Yardstick::Rake::Measurement.new(:yardstick_measure) do |measurement|
|
|
22
23
|
measurement.output = 'tmp/yard_coverage.txt'
|
23
24
|
end
|
24
25
|
|
25
|
-
task quality:
|
26
|
+
task quality: %i(rubocop yardstick_measure)
|
26
27
|
|
27
28
|
task default: [:spec]
|
data/lib/sugar_utils/file.rb
CHANGED
@@ -3,77 +3,101 @@
|
|
3
3
|
require 'solid_assert'
|
4
4
|
require 'fileutils'
|
5
5
|
require 'multi_json'
|
6
|
+
require 'timeout'
|
6
7
|
|
7
8
|
module SugarUtils
|
8
9
|
module File
|
9
10
|
class Error < StandardError; end
|
10
11
|
|
11
|
-
#
|
12
|
+
# @param [File] file
|
13
|
+
# @param [Hash] options
|
14
|
+
# @option options [Integer] :timeout (10)
|
12
15
|
#
|
13
|
-
# @
|
16
|
+
# @raise [Timeout::Error]
|
14
17
|
#
|
18
|
+
# @return [void]
|
19
|
+
def self.flock_shared(file, options = {})
|
20
|
+
timeout = options[:timeout] || 10
|
21
|
+
Timeout.timeout(timeout) { file.flock(::File::LOCK_SH) }
|
22
|
+
end
|
23
|
+
|
15
24
|
# @param [File] file
|
16
|
-
# @param [File::LOCK_EX, File::LOCK_SH] locking_constant
|
17
25
|
# @param [Hash] options
|
18
26
|
# @option options [Integer] :timeout (10)
|
19
27
|
#
|
28
|
+
# @raise [Timeout::Error]
|
29
|
+
#
|
20
30
|
# @return [void]
|
21
|
-
def self.
|
31
|
+
def self.flock_exclusive(file, options = {})
|
22
32
|
timeout = options[:timeout] || 10
|
23
|
-
Timeout.timeout(timeout) { file.flock(
|
33
|
+
Timeout.timeout(timeout) { file.flock(::File::LOCK_EX) }
|
24
34
|
end
|
25
35
|
|
26
36
|
# @param [String] filename
|
27
37
|
# @param [Hash] options
|
28
38
|
# @option options [Integer] :timeout (10)
|
29
39
|
# @option options [Boolean] :raise_on_missing (true)
|
40
|
+
# @option options [String] :value_on_missing ('') which specifies the
|
41
|
+
# value to return if the file is missing and raise_on_missing is false
|
30
42
|
#
|
31
|
-
# @raise [SugarUtils::File::Error
|
43
|
+
# @raise [SugarUtils::File::Error]
|
32
44
|
#
|
33
|
-
# @return [
|
34
|
-
def self.
|
35
|
-
|
36
|
-
|
45
|
+
# @return [String]
|
46
|
+
def self.read(filename, options = {})
|
47
|
+
options[:value_on_missing] ||= ''
|
37
48
|
options[:raise_on_missing] = true if options[:raise_on_missing].nil?
|
38
49
|
|
39
50
|
::File.open(filename, ::File::RDONLY) do |file|
|
40
|
-
|
41
|
-
|
51
|
+
flock_shared(file, options)
|
52
|
+
file.read
|
42
53
|
end
|
43
54
|
rescue SystemCallError, IOError
|
44
55
|
raise(Error, "Cannot read #{filename}") if options[:raise_on_missing]
|
45
|
-
|
46
|
-
rescue MultiJson::ParseError
|
47
|
-
raise(Error, "Cannot parse #{filename}")
|
56
|
+
options[:value_on_missing]
|
48
57
|
rescue Timeout::Error
|
49
58
|
raise(Error, "Cannot read #{filename} because it is locked")
|
50
59
|
end
|
51
60
|
|
52
61
|
# @param [String] filename
|
53
|
-
# @param [
|
62
|
+
# @param [Hash] options
|
63
|
+
# @option options [Integer] :timeout (10)
|
64
|
+
# @option options [Boolean] :raise_on_missing (true)
|
65
|
+
#
|
66
|
+
# @raise [SugarUtils::File::Error]
|
67
|
+
#
|
68
|
+
# @return [Object]
|
69
|
+
def self.read_json(filename, options = {})
|
70
|
+
options[:value_on_missing] = {}
|
71
|
+
MultiJson.load(read(filename, options))
|
72
|
+
rescue MultiJson::ParseError
|
73
|
+
raise(Error, "Cannot parse #{filename}")
|
74
|
+
end
|
75
|
+
|
76
|
+
# @param [String] filename
|
77
|
+
# @param [#to_s] data
|
54
78
|
# @param [Hash] options
|
55
79
|
# @option options [Integer] :timeout (10)
|
56
80
|
# @option options [Boolean] :flush (false)
|
57
81
|
# @option options [Integer] :perm (0666)
|
58
82
|
#
|
59
|
-
# @raise [SugarUtils::File::Error
|
83
|
+
# @raise [SugarUtils::File::Error]
|
60
84
|
#
|
61
85
|
# @return [void]
|
62
|
-
def self.
|
63
|
-
perm = options[:perm] ||
|
86
|
+
def self.write(filename, data, options = {})
|
87
|
+
perm = options[:perm] || 0o666
|
64
88
|
flush = options[:flush] || false
|
65
89
|
|
66
90
|
FileUtils.mkdir_p(::File.dirname(filename))
|
67
91
|
::File.open(filename, ::File::RDWR | ::File::CREAT, perm) do |file|
|
68
|
-
|
92
|
+
flock_exclusive(file, options)
|
69
93
|
|
70
94
|
file.truncate(0) # Ensure file is empty before proceeding.
|
71
|
-
file.puts(
|
95
|
+
file.puts(data.to_s)
|
72
96
|
|
73
97
|
# Flush and fsync to be 100% sure we write this data out now because we
|
74
98
|
# are often reading it immediately and if the OS is buffering, it is
|
75
|
-
# possible we might read it before it is been physically written to
|
76
|
-
# We are not worried about speed here, so this should be OKAY.
|
99
|
+
# possible we might read it before it is been physically written to
|
100
|
+
# disk. We are not worried about speed here, so this should be OKAY.
|
77
101
|
if flush
|
78
102
|
file.flush
|
79
103
|
file.fsync
|
@@ -87,5 +111,19 @@ module SugarUtils
|
|
87
111
|
rescue SystemCallError, IOError => boom
|
88
112
|
raise(Error, "Unable to write #{filename} with #{boom}")
|
89
113
|
end
|
114
|
+
|
115
|
+
# @param [String] filename
|
116
|
+
# @param [#to_json] data
|
117
|
+
# @param [Hash] options
|
118
|
+
# @option options [Integer] :timeout (10)
|
119
|
+
# @option options [Boolean] :flush (false)
|
120
|
+
# @option options [Integer] :perm (0666)
|
121
|
+
#
|
122
|
+
# @raise [SugarUtils::File::Error]
|
123
|
+
#
|
124
|
+
# @return [void]
|
125
|
+
def self.write_json(filename, data, options = {})
|
126
|
+
write(filename, MultiJson.dump(data, pretty: true), options)
|
127
|
+
end
|
90
128
|
end
|
91
129
|
end
|
data/lib/sugar_utils/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -2,13 +2,54 @@
|
|
2
2
|
|
3
3
|
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
4
4
|
require 'sugar_utils'
|
5
|
-
require 'simplecov'
|
6
5
|
require 'rspec/tabular'
|
7
6
|
require 'fakefs/spec_helpers'
|
8
|
-
|
7
|
+
# HACK: including pp seems to resolve an error with FakeFS and File.read
|
8
|
+
# This seems to be related to but not the same as the problem mentioned in the
|
9
|
+
# README
|
10
|
+
# https://github.com/fakefs/fakefs#fakefs-vs-pp-----typeerror-superclass-mismatch-for-class-file
|
11
|
+
require 'pp'
|
12
|
+
|
13
|
+
# Setup code coverage
|
14
|
+
require 'simplecov'
|
15
|
+
SimpleCov.start
|
9
16
|
|
10
17
|
SolidAssert.enable_assertions
|
11
18
|
|
12
19
|
RSpec.configure do |config|
|
20
|
+
# rubocop:disable Style/MixinGrouping
|
13
21
|
config.include FakeFS::SpecHelpers, fakefs: true
|
22
|
+
# rubocop:enable all
|
23
|
+
end
|
24
|
+
|
25
|
+
RSpec::Matchers.define :have_json_content do |expected|
|
26
|
+
match do |actual|
|
27
|
+
next false unless File.exist?(actual)
|
28
|
+
|
29
|
+
@actual = MultiJson.load(File.read(actual))
|
30
|
+
values_match?(expected, @actual)
|
31
|
+
end
|
32
|
+
|
33
|
+
diffable
|
34
|
+
end
|
35
|
+
|
36
|
+
RSpec::Matchers.define :have_content do |expected|
|
37
|
+
match do |actual|
|
38
|
+
next false unless File.exist?(actual)
|
39
|
+
|
40
|
+
@actual = File.open(actual, 'r') { |f| f.read.chomp }
|
41
|
+
values_match?(expected, @actual)
|
42
|
+
end
|
43
|
+
|
44
|
+
diffable
|
45
|
+
end
|
46
|
+
|
47
|
+
RSpec::Matchers.define :have_file_permission do |expected|
|
48
|
+
match do |actual|
|
49
|
+
next false unless File.exist?(actual)
|
50
|
+
|
51
|
+
@actual = format('%o', File.stat(filename).mode)
|
52
|
+
@expected = format('%o', expected)
|
53
|
+
values_match?(@expected, @actual)
|
54
|
+
end
|
14
55
|
end
|
@@ -1,15 +1,14 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
|
-
require 'fileutils'
|
5
4
|
|
6
5
|
describe SugarUtils::File do
|
7
|
-
describe '.
|
8
|
-
subject { described_class.
|
6
|
+
describe '.flock_shared' do
|
7
|
+
subject { described_class.flock_shared(file, options) }
|
9
8
|
let(:file) { instance_double(File) }
|
10
9
|
before do
|
11
|
-
|
12
|
-
expect(file).to receive(:flock).with(
|
10
|
+
allow(Timeout).to receive(:timeout).with(expected_timeout).and_yield
|
11
|
+
expect(file).to receive(:flock).with(::File::LOCK_SH)
|
13
12
|
end
|
14
13
|
|
15
14
|
inputs :options, :expected_timeout
|
@@ -18,59 +17,90 @@ describe SugarUtils::File do
|
|
18
17
|
side_effects_with Hash[timeout: 5], 5
|
19
18
|
end
|
20
19
|
|
21
|
-
describe '.
|
22
|
-
subject { described_class.
|
20
|
+
describe '.flock_exclusive' do
|
21
|
+
subject { described_class.flock_exclusive(file, options) }
|
22
|
+
let(:file) { instance_double(File) }
|
23
|
+
before do
|
24
|
+
allow(Timeout).to receive(:timeout).with(expected_timeout).and_yield
|
25
|
+
expect(file).to receive(:flock).with(::File::LOCK_EX)
|
26
|
+
end
|
23
27
|
|
24
|
-
|
28
|
+
inputs :options, :expected_timeout
|
29
|
+
side_effects_with Hash[], 10
|
30
|
+
side_effects_with Hash[timeout: nil], 10
|
31
|
+
side_effects_with Hash[timeout: 5], 5
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '.read', :fakefs do
|
35
|
+
subject { described_class.read('filename', options) }
|
36
|
+
|
37
|
+
shared_examples_for 'handles the missing file error' do
|
38
|
+
# rubocop:disable Metrics/LineLength
|
25
39
|
inputs :options
|
26
|
-
raise_error_with Hash[],
|
27
|
-
raise_error_with Hash[],
|
28
|
-
raise_error_with Hash[raise_on_missing: true],
|
29
|
-
raise_error_with Hash[raise_on_missing: true],
|
30
|
-
it_with Hash[raise_on_missing: false],
|
40
|
+
raise_error_with Hash[], described_class::Error
|
41
|
+
raise_error_with Hash[], 'Cannot read filename'
|
42
|
+
raise_error_with Hash[raise_on_missing: true], described_class::Error
|
43
|
+
raise_error_with Hash[raise_on_missing: true], 'Cannot read filename'
|
44
|
+
it_with Hash[raise_on_missing: false], ''
|
45
|
+
it_with Hash[raise_on_missing: false, value_on_missing: 'hi'], 'hi'
|
46
|
+
# rubocop:enable all
|
31
47
|
end
|
32
48
|
|
33
|
-
context 'file
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
context 'SysteCallError' do
|
38
|
-
let(:options) { {} }
|
39
|
-
let(:content) { '' }
|
40
|
-
let(:exception) { SystemCallError.new(nil) }
|
41
|
-
before { allow(File).to receive(:open).and_raise(exception) }
|
42
|
-
it { expect_raise_error('Cannot read filename.json') }
|
43
|
-
end
|
49
|
+
context 'missing file' do
|
50
|
+
it_behaves_like 'handles the missing file error'
|
51
|
+
end
|
44
52
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
53
|
+
context 'with IOError' do
|
54
|
+
before { allow(File).to receive(:open).and_raise(IOError) }
|
55
|
+
it_behaves_like 'handles the missing file error'
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'file present' do
|
59
|
+
let(:options) { { key: :value } }
|
60
|
+
before { write('filename', 'content') }
|
52
61
|
|
53
62
|
context 'and locked' do
|
54
|
-
|
55
|
-
|
56
|
-
|
63
|
+
before do
|
64
|
+
expect(described_class).to receive(:flock_shared)
|
65
|
+
.with(kind_of(File), options)
|
66
|
+
.and_raise(Timeout::Error)
|
67
|
+
end
|
68
|
+
it { expect_raise_error('Cannot read filename because it is locked') }
|
57
69
|
end
|
58
70
|
|
59
71
|
context 'and unlocked' do
|
60
|
-
before
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
it_with Hash['key' => 'value'].to_json, Hash['key' => 'value']
|
72
|
+
before do
|
73
|
+
expect(described_class).to receive(:flock_shared)
|
74
|
+
.with(kind_of(File), options)
|
75
|
+
end
|
76
|
+
it { is_expected.to eq('content') }
|
66
77
|
end
|
67
78
|
end
|
68
79
|
end
|
69
80
|
|
70
|
-
describe '.
|
71
|
-
subject
|
72
|
-
|
73
|
-
|
81
|
+
describe '.read_json', :fakefs do
|
82
|
+
subject do
|
83
|
+
described_class.read_json(
|
84
|
+
:filename, key: :value, value_on_missing: :foobar
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
before do
|
89
|
+
allow(described_class).to receive(:read)
|
90
|
+
.with(:filename, key: :value, value_on_missing: {})
|
91
|
+
.and_return(file_content)
|
92
|
+
end
|
93
|
+
|
94
|
+
inputs :file_content
|
95
|
+
raise_error_with 'I am not json', described_class::Error
|
96
|
+
raise_error_with 'I am not json', 'Cannot parse filename'
|
97
|
+
it_with Hash['key' => 'value'].to_json, Hash['key' => 'value']
|
98
|
+
end
|
99
|
+
|
100
|
+
describe '.write', :fakefs do
|
101
|
+
subject { described_class.write(filename, data, options) }
|
102
|
+
let(:data) { 'content' }
|
103
|
+
let(:filename) { 'dir1/dir2/filename' }
|
74
104
|
|
75
105
|
context 'SystemCallError' do
|
76
106
|
let(:options) { {} }
|
@@ -88,36 +118,37 @@ describe SugarUtils::File do
|
|
88
118
|
|
89
119
|
context 'locked' do
|
90
120
|
let(:options) { {} }
|
91
|
-
before
|
121
|
+
before do
|
122
|
+
expect(described_class).to receive(:flock_exclusive)
|
123
|
+
.with(kind_of(File), options)
|
124
|
+
.and_raise(Timeout::Error)
|
125
|
+
end
|
92
126
|
it { expect_raise_error("Unable to write #{filename} because it is locked") }
|
93
127
|
end
|
94
128
|
|
95
129
|
context 'unlocked' do
|
96
130
|
shared_examples_for 'file is written' do
|
97
|
-
before
|
131
|
+
before do
|
132
|
+
expect(described_class).to receive(:flock_exclusive)
|
133
|
+
.with(kind_of(File), options)
|
134
|
+
end
|
98
135
|
|
99
136
|
context 'default options' do
|
100
137
|
let(:options) { {} }
|
101
138
|
before { subject }
|
102
|
-
specify
|
103
|
-
|
104
|
-
expect(MultiJson.load(File.read(filename))).to eq(data)
|
105
|
-
expect(sprintf('%o', File.stat(filename).mode)).to eq('100666')
|
106
|
-
end
|
139
|
+
specify { expect(filename).to have_content(data) }
|
140
|
+
specify { expect(filename).to have_file_permission(0o100666) }
|
107
141
|
end
|
108
142
|
|
109
143
|
context 'options' do
|
110
|
-
let(:options) { { flush: true, perm:
|
144
|
+
let(:options) { { flush: true, perm: 0o600 } }
|
111
145
|
before do
|
112
146
|
expect_any_instance_of(File).to receive(:flush)
|
113
147
|
expect_any_instance_of(File).to receive(:fsync)
|
114
148
|
subject
|
115
149
|
end
|
116
|
-
specify
|
117
|
-
|
118
|
-
expect(MultiJson.load(File.read(filename))).to eq(data)
|
119
|
-
expect(sprintf('%o', File.stat(filename).mode)).to eq('100600')
|
120
|
-
end
|
150
|
+
specify { expect(filename).to have_content(data) }
|
151
|
+
specify { expect(filename).to have_file_permission(0o100600) }
|
121
152
|
end
|
122
153
|
end
|
123
154
|
|
@@ -126,7 +157,7 @@ describe SugarUtils::File do
|
|
126
157
|
end
|
127
158
|
|
128
159
|
context 'and exists' do
|
129
|
-
before { write(filename, 'foobar',
|
160
|
+
before { write(filename, 'foobar', 0o777) }
|
130
161
|
context 'not locked' do
|
131
162
|
it_behaves_like 'file is written'
|
132
163
|
end
|
@@ -134,15 +165,21 @@ describe SugarUtils::File do
|
|
134
165
|
end
|
135
166
|
end
|
136
167
|
|
137
|
-
|
168
|
+
describe '.write_json', :fakefs do
|
169
|
+
subject { described_class.write_json(:filename, data, :options) }
|
138
170
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
171
|
+
let(:data) { { 'key' => 'value' } }
|
172
|
+
before do
|
173
|
+
expect(described_class).to receive(:write).with(
|
174
|
+
:filename, MultiJson.dump(data, pretty: true), :options
|
175
|
+
)
|
176
|
+
end
|
177
|
+
|
178
|
+
specify { subject }
|
144
179
|
end
|
145
180
|
|
181
|
+
##############################################################################
|
182
|
+
|
146
183
|
# @param [String] message
|
147
184
|
def expect_raise_error(message)
|
148
185
|
expect { subject }.to raise_error(described_class::Error, message)
|
@@ -163,6 +200,4 @@ describe SugarUtils::File do
|
|
163
200
|
File.write(filename, content)
|
164
201
|
FileUtils.chmod(perm, filename) if perm
|
165
202
|
end
|
166
|
-
|
167
|
-
|
168
203
|
end
|
data/sugar_utils.gemspec
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# coding: utf-8
|
2
|
+
|
2
3
|
lib = File.expand_path('../lib', __FILE__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
5
|
require 'sugar_utils/version'
|
@@ -17,16 +18,19 @@ Gem::Specification.new do |spec|
|
|
17
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
19
|
spec.require_paths = ['lib']
|
19
20
|
|
21
|
+
spec.required_ruby_version = '>= 2.0.0'
|
22
|
+
|
20
23
|
spec.add_dependency 'multi_json', '~> 1.12.1'
|
21
24
|
spec.add_dependency 'solid_assert', '~> 1.0'
|
22
25
|
|
23
26
|
spec.add_development_dependency 'bundler', '~> 1.7'
|
24
|
-
spec.add_development_dependency 'rake', '~>
|
25
|
-
spec.add_development_dependency 'rspec', '~> 3.
|
27
|
+
spec.add_development_dependency 'rake', '~> 12.0'
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.5.0'
|
26
29
|
spec.add_development_dependency 'rspec-tabular', '~> 0.1.0'
|
27
|
-
spec.add_development_dependency 'simplecov', '~> 0.
|
28
|
-
spec.add_development_dependency '
|
29
|
-
spec.add_development_dependency 'yard', '~> 0.8.7.6'
|
30
|
+
spec.add_development_dependency 'simplecov', '~> 0.14.0'
|
31
|
+
spec.add_development_dependency 'yard', '~> 0.9.0'
|
30
32
|
spec.add_development_dependency 'yardstick', '~> 0.9.9'
|
31
33
|
spec.add_development_dependency 'fakefs', '~> 0.7'
|
34
|
+
spec.add_development_dependency 'codeclimate-test-reporter'
|
35
|
+
spec.add_development_dependency 'rubocop'
|
32
36
|
end
|
metadata
CHANGED
@@ -1,169 +1,183 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sugar_utils
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Sullivan Cant
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-03-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: multi_json
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 1.12.1
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 1.12.1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: solid_assert
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - ~>
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '1.0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - ~>
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - ~>
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '1.7'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - ~>
|
52
|
+
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '1.7'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - ~>
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '12.0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - ~>
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '12.0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rspec
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - ~>
|
73
|
+
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: 3.
|
75
|
+
version: 3.5.0
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- - ~>
|
80
|
+
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 3.
|
82
|
+
version: 3.5.0
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: rspec-tabular
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - ~>
|
87
|
+
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: 0.1.0
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - ~>
|
94
|
+
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: 0.1.0
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: simplecov
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - ~>
|
101
|
+
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: 0.
|
103
|
+
version: 0.14.0
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - ~>
|
108
|
+
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: 0.
|
110
|
+
version: 0.14.0
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
112
|
+
name: yard
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- - ~>
|
115
|
+
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version:
|
117
|
+
version: 0.9.0
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- - ~>
|
122
|
+
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version:
|
124
|
+
version: 0.9.0
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
126
|
+
name: yardstick
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
|
-
- - ~>
|
129
|
+
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version: 0.
|
131
|
+
version: 0.9.9
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
|
-
- - ~>
|
136
|
+
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version: 0.
|
138
|
+
version: 0.9.9
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
140
|
+
name: fakefs
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
142
142
|
requirements:
|
143
|
-
- - ~>
|
143
|
+
- - "~>"
|
144
144
|
- !ruby/object:Gem::Version
|
145
|
-
version: 0.
|
145
|
+
version: '0.7'
|
146
146
|
type: :development
|
147
147
|
prerelease: false
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
149
149
|
requirements:
|
150
|
-
- - ~>
|
150
|
+
- - "~>"
|
151
151
|
- !ruby/object:Gem::Version
|
152
|
-
version: 0.
|
152
|
+
version: '0.7'
|
153
153
|
- !ruby/object:Gem::Dependency
|
154
|
-
name:
|
154
|
+
name: codeclimate-test-reporter
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
156
156
|
requirements:
|
157
|
-
- -
|
157
|
+
- - ">="
|
158
158
|
- !ruby/object:Gem::Version
|
159
|
-
version: '0
|
159
|
+
version: '0'
|
160
160
|
type: :development
|
161
161
|
prerelease: false
|
162
162
|
version_requirements: !ruby/object:Gem::Requirement
|
163
163
|
requirements:
|
164
|
-
- -
|
164
|
+
- - ">="
|
165
165
|
- !ruby/object:Gem::Version
|
166
|
-
version: '0
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: rubocop
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
167
181
|
description:
|
168
182
|
email:
|
169
183
|
- acant@sugarcrm.com
|
@@ -171,10 +185,10 @@ executables: []
|
|
171
185
|
extensions: []
|
172
186
|
extra_rdoc_files: []
|
173
187
|
files:
|
174
|
-
- .gitignore
|
175
|
-
- .rspec
|
176
|
-
- .rubocop.yml
|
177
|
-
- .travis.yml
|
188
|
+
- ".gitignore"
|
189
|
+
- ".rspec"
|
190
|
+
- ".rubocop.yml"
|
191
|
+
- ".travis.yml"
|
178
192
|
- CHANGELOG.md
|
179
193
|
- CONTRIBUTING.md
|
180
194
|
- CONTRIBUTOR_TERMS.md
|
@@ -199,17 +213,17 @@ require_paths:
|
|
199
213
|
- lib
|
200
214
|
required_ruby_version: !ruby/object:Gem::Requirement
|
201
215
|
requirements:
|
202
|
-
- -
|
216
|
+
- - ">="
|
203
217
|
- !ruby/object:Gem::Version
|
204
|
-
version:
|
218
|
+
version: 2.0.0
|
205
219
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
206
220
|
requirements:
|
207
|
-
- -
|
221
|
+
- - ">="
|
208
222
|
- !ruby/object:Gem::Version
|
209
223
|
version: '0'
|
210
224
|
requirements: []
|
211
225
|
rubyforge_project:
|
212
|
-
rubygems_version: 2.
|
226
|
+
rubygems_version: 2.6.10
|
213
227
|
signing_key:
|
214
228
|
specification_version: 4
|
215
229
|
summary: Utility methods extracted from SugarCRM Ruby projects.
|
@@ -217,4 +231,3 @@ test_files:
|
|
217
231
|
- spec/spec_helper.rb
|
218
232
|
- spec/sugar_utils/file_spec.rb
|
219
233
|
- spec/sugar_utils_spec.rb
|
220
|
-
has_rdoc:
|