Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

memoize version_select #10

Merged
merged 8 commits into from
Aug 27, 2017
Merged

memoize version_select #10

merged 8 commits into from
Aug 27, 2017

Conversation

djbe
Copy link
Member

@djbe djbe commented Aug 24, 2017

Memoize the result of version_select, and allow us to change it from the Rakefile using a MIN_XCODE_VERSION constant

@djbe djbe requested a review from AliSoftware August 24, 2017 12:36
@@ -17,7 +18,7 @@ class Utils
# run a command using xcrun and xcpretty if applicable
def self.run(cmd, task, subtask = '', xcrun: false, formatter: :raw)
commands = xcrun ? [*cmd].map { |cmd|
"#{version_select} xcrun #{cmd}"
"#{@@version_select} xcrun #{cmd}"
Copy link
Contributor

@AliSoftware AliSoftware Aug 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The concept of the memoization is to still always call the method — which will either return immediately with the cached version, or compute it and cache it if it's the first time it's requested.

So don't change the call sites, otherwise you risk using an empty value if the value was never cached

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

# if current Xcode already fulfills min version don't force DEVELOPER_DIR=…
current_xcode = Pathname.new(`xcode-select -p`).parent.parent
current_xcode_version = `mdls -name kMDItemVersion -raw #{current_xcode}`.chomp
if current_xcode_version.to_f >= MIN_XCODE_VERSION
Copy link
Contributor

@AliSoftware AliSoftware Aug 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer the return "" if … construct, similar to a guard in Swift, to avoid having to indent the whole rest in a big else.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NB: You could try to run rubocop on the ruby files to check for style (that's the ruby equivalent of swiftlint)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That gave me a
LocalJumpError: unexpected return

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, right, you're in the begin block here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I do

  def self.version_select
    @version_select ||= begin
      print "------ Checking xcode version ----"
      # if current Xcode already fulfills min version don't force DEVELOPER_DIR=…
      current_xcode = Pathname.new(`xcode-select -p`).parent.parent
      current_xcode_version = `mdls -name kMDItemVersion -raw #{current_xcode}`.chomp
      return "" if current_xcode_version.to_f >= MIN_XCODE_VERSION

The return interrupts the memoization and never sets the variable.

end
private_class_method :version_select
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not private anymore?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because it isn't a method.

Copy link
Contributor

@AliSoftware AliSoftware Aug 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, version_select is still a def'd method.

@@version_select is the property caching its result, but the method is still there. So we have to keep it private.

It's like if in Swift you had

func version_select() -> String {
  if let cached_vs = cached_vs { return cached_vs }
   compute result
  cached_vs = result
}

version_select is still a method, using a property named cached_vs / @@version_select to store its cached value.


Idea: what we could do instead to make that code less complex is split that in two:

def self.compute_developer_dir
  # Same content as the version_select without memoizing, so basically what you have in that `begin…end` block
end

def self.version_select
  @@version_select ||= self.compute_developer_dir
end

Not sure what you prefer (but at least that would allow us an early return in self.compute_developer_dir)

…m the Rakefile using a MIN_XCODE_VERSION constant
@djbe djbe force-pushed the feature/memoize-version-select branch from 62e59bf to e3eee22 Compare August 24, 2017 13:49
@djbe
Copy link
Member Author

djbe commented Aug 24, 2017

FYI rubocop gives a massive number of warnings (115 to be exact, for 6 files).

@djbe djbe force-pushed the feature/memoize-version-select branch from bedce7a to 6bbcb7b Compare August 24, 2017 14:07
@AliSoftware
Copy link
Contributor

Yeah sadly not surprising, I'm not sure I ran it in a while

@@ -3,15 +3,15 @@

namespace :changelog do
desc 'Add the empty CHANGELOG entries after a new release'
task :reset do |task|
task :reset do |_task|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't use any of the parameters you can also completely remove them (do instead of do |task|)

@@ -65,6 +68,6 @@ namespace :changelog do
body = Utils.top_changelog_entry

repo_name = File.basename(`git remote get-url origin`.chomp, '.git').freeze
client.create_release("SwiftGen/#{repo_name}", tag, :body => body)
client.create_release("SwiftGen/#{repo_name}", tag, body: body)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 does that with with system ruby? I though that syntax was Ruby 2.1 or 2.2 only?

Copy link
Member Author

@djbe djbe Aug 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Afaik, it's ruby 1.9 syntax:

utils.rake:49:25: C: [Corrected] Use the new Ruby 1.9 hash syntax.
Octokit::Client.new(:access_token => token)
_____________________^^^^^^^^^^^^^^^

@djbe djbe force-pushed the feature/memoize-version-select branch from 6bbcb7b to 31749c7 Compare August 24, 2017 16:21
if File.directory?('Sources')
desc 'Lint the code'
task :code => :install do |task|
task code: :install do |task|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't change this. Even if technically correct when considering this as a Ruby hash (and only in recent versions of Ruby), Rake used that syntax on purpose

Technically, Rake defines a def task(params) method internally to define rake tasks, and allows you to write:

  • task :foo do to define a simple task without dependency. Which in practice is just calling task() with a single parameter :foo (of type symbol), defining the name of the task
  • task :code => :install do to define a task with a single dependency. The use of => is expected to give the visual meaning of dependency. In practice what you do is pass a single parameter to that task method which happens to be a Hash (dictionary) with a single key (:code) and a value (:install)… but that's implementation detail
  • task :code => [:x, :y, :z] do to define multiple dependencies, once again that => arrow is supposed to represent the visual cue of dependency, even if in practice what you do is pass a single parameter of type Hash with a single key :code, whose value is an array of 3 values… but again that's an implementation detail

So in practice Rake chose to use that implementation detail because that => was a nice visual cue for dependency (and not because it was technically a hash, which I actually never realised it was in that case until recently). So I think for the special case of task declarations, it's better to keep the => syntax, whatever rubocop says.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@djbe djbe force-pushed the feature/memoize-version-select branch from 31749c7 to a2e321d Compare August 24, 2017 17:50
@djbe djbe merged commit cc38b8a into master Aug 27, 2017
@djbe djbe deleted the feature/memoize-version-select branch August 27, 2017 17:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants