doggy 2.0.42 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ inherit_from: http://shopify.github.io/ruby-style-guide/rubocop.yml
2
+
3
+ AllCops:
4
+ TargetRubyVersion: 2.6
5
+ Exclude:
6
+ - bin/*
7
+ - db/schema.rb
8
+ - node_modules/**/*
9
+
10
+ Rails:
11
+ Enabled: false
12
+
13
+ Style/ClassAndModuleChildren:
14
+ Enabled: false
15
+
16
+ Style/WordArray:
17
+ MinSize: 2
18
+
19
+ Metrics/LineLength:
20
+ Enabled: false
@@ -1,3 +1,3 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.3.1
3
+ - 2.5.5
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  gemspec
@@ -1,60 +1,79 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- doggy (2.0.42)
5
- activesupport (~> 4)
6
- json (~> 1.8.3)
7
- parallel (~> 1.6.1)
8
- rugged (~> 0.23.2)
9
- thor (~> 0.19.1)
10
- virtus (~> 1.0.5)
4
+ doggy (2.1.0)
5
+ activesupport
6
+ parallel
7
+ rugged
8
+ thor
9
+ virtus
11
10
 
12
11
  GEM
13
12
  remote: https://rubygems.org/
14
13
  specs:
15
- activesupport (4.2.10)
16
- i18n (~> 0.7)
14
+ activesupport (5.2.3)
15
+ concurrent-ruby (~> 1.0, >= 1.0.2)
16
+ i18n (>= 0.7, < 2)
17
17
  minitest (~> 5.1)
18
- thread_safe (~> 0.3, >= 0.3.4)
19
18
  tzinfo (~> 1.1)
20
- addressable (2.5.0)
21
- public_suffix (~> 2.0, >= 2.0.2)
19
+ addressable (2.6.0)
20
+ public_suffix (>= 2.0.2, < 4.0)
21
+ ast (2.4.0)
22
22
  axiom-types (0.1.1)
23
23
  descendants_tracker (~> 0.0.4)
24
24
  ice_nine (~> 0.11.0)
25
25
  thread_safe (~> 0.3, >= 0.3.1)
26
+ coderay (1.1.2)
26
27
  coercible (1.0.0)
27
28
  descendants_tracker (~> 0.0.1)
28
- concurrent-ruby (1.0.5)
29
+ concurrent-ruby (1.1.5)
29
30
  crack (0.4.3)
30
31
  safe_yaml (~> 1.0.0)
31
32
  descendants_tracker (0.0.4)
32
33
  thread_safe (~> 0.3, >= 0.3.1)
33
34
  equalizer (0.0.11)
34
- hashdiff (0.2.3)
35
- i18n (0.9.3)
35
+ hashdiff (0.3.8)
36
+ i18n (1.6.0)
36
37
  concurrent-ruby (~> 1.0)
37
38
  ice_nine (0.11.2)
38
- json (1.8.6)
39
+ jaro_winkler (1.5.2)
39
40
  metaclass (0.0.4)
40
- minitest (5.9.0)
41
- mocha (1.1.0)
41
+ method_source (0.9.2)
42
+ minitest (5.11.3)
43
+ mocha (1.8.0)
42
44
  metaclass (~> 0.0.1)
43
- parallel (1.6.2)
44
- public_suffix (2.0.4)
45
- rake (10.5.0)
46
- rugged (0.23.3)
47
- safe_yaml (1.0.4)
48
- thor (0.19.4)
45
+ parallel (1.17.0)
46
+ parser (2.6.2.1)
47
+ ast (~> 2.4.0)
48
+ pry (0.12.2)
49
+ coderay (~> 1.1.0)
50
+ method_source (~> 0.9.0)
51
+ psych (3.1.0)
52
+ public_suffix (3.0.3)
53
+ rainbow (3.0.0)
54
+ rake (12.3.2)
55
+ rubocop (0.67.2)
56
+ jaro_winkler (~> 1.5.1)
57
+ parallel (~> 1.10)
58
+ parser (>= 2.5, != 2.5.1.1)
59
+ psych (>= 3.1.0)
60
+ rainbow (>= 2.2.2, < 4.0)
61
+ ruby-progressbar (~> 1.7)
62
+ unicode-display_width (>= 1.4.0, < 1.6)
63
+ ruby-progressbar (1.10.0)
64
+ rugged (0.28.1)
65
+ safe_yaml (1.0.5)
66
+ thor (0.20.3)
49
67
  thread_safe (0.3.6)
50
- tzinfo (1.2.4)
68
+ tzinfo (1.2.5)
51
69
  thread_safe (~> 0.1)
70
+ unicode-display_width (1.5.0)
52
71
  virtus (1.0.5)
53
72
  axiom-types (~> 0.1)
54
73
  coercible (~> 1.0)
55
74
  descendants_tracker (~> 0.0, >= 0.0.3)
56
75
  equalizer (~> 0.0, >= 0.0.9)
57
- webmock (1.22.6)
76
+ webmock (3.5.1)
58
77
  addressable (>= 2.3.6)
59
78
  crack (>= 0.3.2)
60
79
  hashdiff
@@ -63,12 +82,14 @@ PLATFORMS
63
82
  ruby
64
83
 
65
84
  DEPENDENCIES
66
- bundler (~> 1.10)
85
+ bundler
67
86
  doggy!
68
87
  minitest
69
88
  mocha
70
- rake (~> 10.0)
89
+ pry
90
+ rake
91
+ rubocop
71
92
  webmock
72
93
 
73
94
  BUNDLED WITH
74
- 1.16.0
95
+ 2.0.1
data/README.md CHANGED
@@ -81,7 +81,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
81
81
 
82
82
  ## Contributing
83
83
 
84
- 1. Fork it ( https://github.com/bai/doggy/fork )
84
+ 1. Fork it ( https://github.com/shopify/doggy/fork )
85
85
  2. Create your feature branch (`git checkout -b my-new-feature`)
86
86
  3. Commit your changes (`git commit -am 'Add some feature'`)
87
87
  4. Push to the branch (`git push origin my-new-feature`)
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/gem_tasks"
2
4
  require "rake/testtask"
3
5
 
@@ -7,4 +9,4 @@ Rake::TestTask.new(:test) do |t|
7
9
  t.test_files = FileList['test/**/*_test.rb']
8
10
  end
9
11
 
10
- task :default => :test
12
+ task(default: :test)
data/dev.yml ADDED
@@ -0,0 +1,8 @@
1
+ name: doggy
2
+
3
+ up:
4
+ # - homebrew:
5
+ # - cmake
6
+ - ruby:
7
+ version: 2.6.2
8
+ - bundler
@@ -1,4 +1,6 @@
1
1
  # coding: utf-8
2
+ # frozen_string_literal: true
3
+
2
4
  lib = File.expand_path('../lib', __FILE__)
3
5
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
6
  require 'doggy/version'
@@ -9,26 +11,27 @@ Gem::Specification.new do |spec|
9
11
  spec.authors = ["Vlad Gorodetsky", "Andre Medeiros"]
10
12
  spec.email = ["v@gor.io", "me@andremedeiros.info"]
11
13
 
12
- spec.summary = %q{Syncs DataDog dashboards, alerts, screenboards, and monitors.}
13
- spec.description = %q{Syncs and manages DataDog dashboards, alerts, screenboards, and monitors.}
14
+ spec.summary = 'Syncs DataDog dashboards, alerts, screenboards, and monitors.'
15
+ spec.description = 'Syncs and manages DataDog dashboards, alerts, screenboards, and monitors.'
14
16
  spec.homepage = "http://github.com/bai/doggy"
15
17
  spec.license = "MIT"
16
18
 
17
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
+ spec.files = %x(git ls-files -z).split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
20
  spec.bindir = "exe"
19
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
22
  spec.require_paths = ["lib"]
21
23
 
22
- spec.required_ruby_version = '~> 2.0'
24
+ spec.required_ruby_version = '~> 2.5'
23
25
 
24
- spec.add_dependency "json", "~> 1.8.3"
25
- spec.add_dependency "parallel", "~> 1.6.1"
26
- spec.add_dependency "thor", "~> 0.19.1"
27
- spec.add_dependency "virtus", "~> 1.0.5"
28
- spec.add_dependency "rugged", "~> 0.23.2"
29
- spec.add_dependency 'activesupport', '~> 4'
26
+ spec.add_runtime_dependency("parallel")
27
+ spec.add_runtime_dependency("thor")
28
+ spec.add_runtime_dependency("virtus")
29
+ spec.add_runtime_dependency("rugged")
30
+ spec.add_runtime_dependency('activesupport')
30
31
 
31
- spec.add_development_dependency "bundler", "~> 1.10"
32
- spec.add_development_dependency "rake", "~> 10.0"
33
- spec.add_development_dependency "minitest"
32
+ spec.add_development_dependency("bundler")
33
+ spec.add_development_dependency("rake")
34
+ spec.add_development_dependency("minitest")
35
+ spec.add_development_dependency('pry')
36
+ spec.add_development_dependency('rubocop')
34
37
  end
data/exe/doggy CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require "doggy"
4
5
  Doggy::CLI.start(ARGV)
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require "pathname"
4
5
  require "net/http"
@@ -45,8 +46,8 @@ module Doggy
45
46
  # TODO: Raise error when root can't be found
46
47
  current_dir = Dir.pwd
47
48
 
48
- while current_dir != '/' do
49
- if File.exist?(File.join(current_dir, 'Gemfile')) then
49
+ while current_dir != '/'
50
+ if File.exist?(File.join(current_dir, 'Gemfile'))
50
51
  return Pathname.new(current_dir)
51
52
  else
52
53
  current_dir = File.expand_path('../', current_dir)
@@ -74,7 +75,6 @@ module Doggy
74
75
  (curr_dir.expand_path(resolved + path) + path).to_s
75
76
  end
76
77
 
77
-
78
78
  protected
79
79
 
80
80
  def secrets
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require "thor"
4
5
 
@@ -79,4 +80,3 @@ module Doggy
79
80
  end
80
81
  end
81
82
  end
82
-
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Doggy
4
5
  class CLI::Delete
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Doggy
4
5
  class CLI::Edit
@@ -9,10 +10,10 @@ module Doggy
9
10
 
10
11
  def run
11
12
  resource = Doggy::Model.find_local(@param)
12
- return Doggy.ui.error("Could not find resource with #{ @param }") unless resource
13
+ return Doggy.ui.error("Could not find resource with #{@param}") unless resource
13
14
 
14
15
  forked_resource = fork(resource)
15
- system("open '#{ forked_resource.human_edit_url }'")
16
+ system("open '#{forked_resource.human_edit_url}'")
16
17
  wait_for_edit
17
18
 
18
19
  new_resource = Doggy::Model.infer_type(resource.attributes).find(forked_resource.id)
@@ -34,9 +35,7 @@ module Doggy
34
35
  private
35
36
 
36
37
  def wait_for_edit
37
- while !Doggy.ui.yes?('Are you done editing?(Y/N)') do
38
- Doggy.ui.say "run, rabbit run / dig that hole, forget the sun / and when at last the work is done / don't sit down / it's time to dig another one"
39
- end
38
+ Doggy.ui.say("run, rabbit run / dig that hole, forget the sun / and when at last the work is done / don't sit down / it's time to dig another one") until Doggy.ui.yes?('Are you done editing?(Y/N)')
40
39
  end
41
40
 
42
41
  def fork(resource)
@@ -44,15 +43,15 @@ module Doggy
44
43
  forked_resource = resource.dup
45
44
  forked_resource.id = nil
46
45
  forked_resource.refute_read_only!
47
- if resource.class.to_s.downcase =~ /dashboard/
48
- forked_resource.title = "[#{ salt }] " + forked_resource.title
49
- forked_resource.description = "[fork of #{ resource.id }] " + forked_resource.title
50
- elsif resource.class.to_s.downcase =~ /screen/
51
- forked_resource.board_title = "[#{ salt }] " + forked_resource.board_title
52
- elsif resource.class.to_s.downcase =~ /monitor/
53
- forked_resource.name = "[#{ salt }] " + forked_resource.name
46
+ if /dashboard/.match?(resource.class.to_s.downcase)
47
+ forked_resource.title = "[#{salt}] " + forked_resource.title
48
+ forked_resource.description = "[fork of #{resource.id}] " + forked_resource.title
49
+ elsif /screen/.match?(resource.class.to_s.downcase)
50
+ forked_resource.board_title = "[#{salt}] " + forked_resource.board_title
51
+ elsif /monitor/.match?(resource.class.to_s.downcase)
52
+ forked_resource.name = "[#{salt}] " + forked_resource.name
54
53
  else
55
- raise StandardError.new('Unknown resource type, cannot edit.')
54
+ raise StandardError, 'Unknown resource type, cannot edit.'
56
55
  end
57
56
  forked_resource.save
58
57
  forked_resource
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'doggy/duration'
4
5
 
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'parallel'
4
5
 
@@ -35,8 +36,8 @@ module Doggy
35
36
  end.compact
36
37
 
37
38
  if local_resources.size != remote_resources.size
38
- normalized_remote_resources = remote_resources.map { |remote_resource| [ remote_resource.class.name, remote_resource.id ] }
39
- normalized_local_resources = local_resources.map { |local_resource| [ local_resource.class.name, local_resource.id ] }
39
+ normalized_remote_resources = remote_resources.map { |remote_resource| [remote_resource.class.name, remote_resource.id] }
40
+ normalized_local_resources = local_resources.map { |local_resource| [local_resource.class.name, local_resource.id] }
40
41
  normalized_resource_diff = Hash[normalized_remote_resources - normalized_local_resources]
41
42
 
42
43
  # Here we traverse `remote_resources` to find remote resource with matching class name and id.
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Doggy
4
5
  class CLI::Push
@@ -8,14 +9,14 @@ module Doggy
8
9
 
9
10
  def sync_changes
10
11
  changed_resources = Doggy::Model.changed_resources
11
- Doggy.ui.say "Syncing #{changed_resources.size} objects to Datadog..."
12
+ Doggy.ui.say("Syncing #{changed_resources.size} objects to Datadog...")
12
13
  changed_resources.each do |resource|
13
14
  if resource.is_deleted
14
- Doggy.ui.say "Deleting #{resource.path}, with id = #{resource.id}"
15
+ Doggy.ui.say("Deleting #{resource.path}, with id = #{resource.id}")
15
16
  resp = resource.destroy
16
- Doggy.ui.say "Response: #{resp.inspect}"
17
+ Doggy.ui.say("Response: #{resp.inspect}")
17
18
  else
18
- Doggy.ui.say "Saving #{resource.path}, with id = #{resource.id}"
19
+ Doggy.ui.say("Saving #{resource.path}, with id = #{resource.id}")
19
20
  resource.ensure_read_only!
20
21
  resource.save
21
22
  end
@@ -30,7 +31,7 @@ module Doggy
30
31
  end
31
32
  Doggy::Model.all_local_resources.each do |resource|
32
33
  next if ids.any? && !ids.include?(resource.id.to_s)
33
- Doggy.ui.say "Pushing #{resource.path}, with id #{resource.id}"
34
+ Doggy.ui.say("Pushing #{resource.path}, with id #{resource.id}")
34
35
  resource.ensure_read_only!
35
36
  resource.save
36
37
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Doggy
4
5
  class CLI::Unmute
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copied from https://github.com/Shopify/spy/blob/ac7bfb9550bfd7bafd191bc31f1bcd9dc4ce9ee6/lib/spy/duration.rb
2
4
  # and edited accordingly
3
5
 
@@ -5,14 +7,14 @@ require 'active_support/core_ext/module'
5
7
  require 'active_support/core_ext/integer'
6
8
 
7
9
  module Duration
8
- DURATION_FORMAT = /
10
+ DURATION_FORMAT = %r{
9
11
  \A
10
12
  (?<days>\d+d)?
11
13
  (?<hours>\d+h)?
12
14
  (?<minutes>\d+m)?
13
15
  (?<seconds>\d+s)?
14
16
  \z
15
- /x
17
+ }x
16
18
  DURATION_UNITS = {
17
19
  's' => :seconds,
18
20
  'm' => :minutes,
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require "json"
4
5
  require "parallel"
@@ -25,18 +26,18 @@ module Doggy
25
26
  def find(id)
26
27
  attributes = request(:get, resource_url(id), nil, [404])
27
28
  return if attributes['errors']
28
- resource = new(attributes)
29
+ resource = new(attributes)
29
30
 
30
31
  resource.loading_source = :remote
31
32
  resource
32
33
  end
33
34
 
34
35
  def find_local(param)
35
- resources = Doggy::Model.all_local_resources
36
- if (id = param).is_a?(Integer) || (param =~ /^[0-9]+$/ && id = Integer(param)) then
36
+ resources = Doggy::Model.all_local_resources
37
+ if (id = param).is_a?(Integer) || (param =~ /^[0-9]+$/ && id = Integer(param))
37
38
  return resources.find { |res| res.id == id }
38
39
  end
39
- if id = param[/(dash\/|screen\/|monitors#)(\d+)/i, 2]
40
+ if id = param[%r{(dash/|screen/|monitors#)(\d+)}i, 2]
40
41
  return resources.find { |res| res.id == Integer(id) }
41
42
  end
42
43
  full_path = File.expand_path(param.gsub('objects/', ''), Doggy.object_root)
@@ -49,7 +50,7 @@ module Doggy
49
50
  begin
50
51
  attributes = JSON.parse(raw)
51
52
  rescue JSON::ParserError
52
- Doggy.ui.error "Could not parse #{ file }."
53
+ Doggy.ui.error("Could not parse #{file}.")
53
54
  next
54
55
  end
55
56
  resource = infer_type(attributes).new(attributes)
@@ -65,13 +66,13 @@ module Doggy
65
66
  diff.find_similar!
66
67
  diff.each_delta.map do |delta|
67
68
  new_file_path = delta.new_file[:path]
68
- next unless new_file_path.match(/\Aobjects\//)
69
+ next unless new_file_path =~ %r{\Aobjects/}
69
70
  is_deleted = delta.status == :deleted
70
71
  oid = is_deleted ? delta.old_file[:oid] : delta.new_file[:oid]
71
72
  begin
72
73
  attributes = JSON.parse(repo.read(oid).data)
73
74
  rescue JSON::ParserError
74
- Doggy.ui.error("Could not parse #{ new_file_path }. Skipping...")
75
+ Doggy.ui.error("Could not parse #{new_file_path}. Skipping...")
75
76
  next
76
77
  end
77
78
  resource = infer_type(attributes).new(attributes)
@@ -83,7 +84,7 @@ module Doggy
83
84
  end
84
85
 
85
86
  def infer_type(attributes)
86
- has_key = ->(key) { attributes.has_key?(key.to_s) || attributes.has_key?(key.to_sym) }
87
+ has_key = ->(key) { attributes.key?(key.to_s) || attributes.key?(key.to_sym) }
87
88
  return Models::Dashboard if has_key.call('graphs')
88
89
  return Models::Monitor if has_key.call('message')
89
90
  return Models::Screen if has_key.call('board_title')
@@ -92,10 +93,10 @@ module Doggy
92
93
  def request(method, url, body = nil, accepted_errors = nil)
93
94
  uri = URI(url)
94
95
 
95
- if uri.query
96
- uri.query = "api_key=#{ Doggy.api_key }&application_key=#{ Doggy.application_key }" + '&' + uri.query
96
+ uri.query = if uri.query
97
+ "api_key=#{Doggy.api_key}&application_key=#{Doggy.application_key}" + '&' + uri.query
97
98
  else
98
- uri.query = "api_key=#{ Doggy.api_key }&application_key=#{ Doggy.application_key }"
99
+ "api_key=#{Doggy.api_key}&application_key=#{Doggy.application_key}"
99
100
  end
100
101
 
101
102
  http = Net::HTTP.new(uri.host, uri.port)
@@ -112,16 +113,16 @@ module Doggy
112
113
  request.body = body if body
113
114
 
114
115
  response = http.request(request)
115
- parsed_response = response.body ? JSON.parse(response.body) : nil
116
+ parsed_response = response.body.present? ? JSON.parse(response.body) : nil
116
117
 
117
118
  unless accepted_response(response.code.to_i, accepted_errors)
118
- raise DoggyError, "Unexpected response code #{response.code} for #{url}, body: #{parsed_response.to_s}"
119
+ raise DoggyError, "Unexpected response code #{response.code} for #{url}, body: #{parsed_response}"
119
120
  end
120
121
  parsed_response
121
122
  end
122
123
 
123
124
  def accepted_response(code, accepted_errors = nil)
124
- if (accepted_errors != nil) && (accepted_errors.include? code)
125
+ if !accepted_errors.nil? && accepted_errors.include?(code)
125
126
  true
126
127
  else
127
128
  code >= 200 && code < 400
@@ -131,7 +132,7 @@ module Doggy
131
132
  def current_sha
132
133
  now = Time.now.to_i
133
134
  month_ago = now - 3600 * 24 * 30
134
- result = request(:get, "https://app.datadoghq.com/api/v1/events?start=#{ month_ago }&end=#{ now }&tags=audit,shipit")
135
+ result = request(:get, "https://app.datadoghq.com/api/v1/events?start=#{month_ago}&end=#{now}&tags=audit,shipit")
135
136
  result['events'][0]['text'] # most recetly deployed SHA
136
137
  end
137
138
 
@@ -144,25 +145,24 @@ module Doggy
144
145
  tags: %w(audit shipit),
145
146
  date_happened: Time.now.to_i,
146
147
  priority: 'normal',
147
- source_type_name: 'shipit'
148
+ source_type_name: 'shipit',
148
149
  }.to_json)
149
150
  end
150
151
 
151
152
  def sort_by_key(hash, &block)
152
- hash.keys.sort(&block).reduce({}) do |seed, key|
153
+ hash.keys.sort(&block).each_with_object({}) do |key, seed|
153
154
  seed[key] = hash[key]
154
155
  if seed[key].is_a?(Hash)
155
156
  seed[key] = Doggy::Model.sort_by_key(seed[key], &block)
156
157
  elsif seed[key].is_a?(Array)
157
158
  seed[key].each_with_index { |e, i| seed[key][i] = sort_by_key(e, &block) if e.is_a?(Hash) }
158
159
  end
159
- seed
160
160
  end
161
161
  end
162
162
 
163
163
  protected
164
164
 
165
- def resource_url(id = nil)
165
+ def resource_url(_id = nil)
166
166
  raise NotImplementedError, "#resource_url has to be implemented."
167
167
  end
168
168
  end # class << self
@@ -200,14 +200,14 @@ module Doggy
200
200
  validate
201
201
 
202
202
  body = JSON.dump(to_h)
203
- if !id then
203
+ if !id
204
204
  attributes = request(:post, resource_url, body)
205
205
  self.id = self.class.new(attributes).id
206
206
  save_local
207
- Doggy.ui.say "Created #{ path }"
207
+ Doggy.ui.say("Created #{path}")
208
208
  else
209
209
  request(:put, resource_url(id), body)
210
- Doggy.ui.say "Updated #{ path }"
210
+ Doggy.ui.say("Updated #{path}")
211
211
  end
212
212
  end
213
213
 
@@ -230,4 +230,3 @@ module Doggy
230
230
  end
231
231
  end # Model
232
232
  end # Doggy
233
-