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

Ruby Rewrite #18

Merged
merged 1 commit into from
Oct 3, 2023
Merged

Ruby Rewrite #18

merged 1 commit into from
Oct 3, 2023

Conversation

murjax
Copy link
Owner

@murjax murjax commented Sep 6, 2023

Rewrite the original Ruby project (currently in this repo under old/ruby).

Improvements made:

  • Remove redundant uptime check ping. If we don't get a response, we just won't do anything.
  • Remove git library dependency. We just run git clone directly. This project no longer requires any dependencies outside of testing.
  • Validate username presence, account presence, and repo info presence before cloning.
  • Stub externals in test. This includes the API request to Github and git clone command. Supported by RSpec method stubbing.
  • Create an executable binary.
  • Arrange files in standard directories (bin, lib).

Issues for Ruby: #22, #27, #44

@murjax murjax marked this pull request as ready for review September 9, 2023 17:41
ruby/README.md Outdated Show resolved Hide resolved
return unless clonable?

Kernel.system(clone_command)
end
Copy link
Collaborator

@MichaelDimmitt MichaelDimmitt Sep 18, 2023

Choose a reason for hiding this comment

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

@murjax
Is call a pattern common in the ruby community? It seems something more descriptive could be helpful long term. The call here is going to hit the api if possible and get all the repos.

So maybe it could be renamed clone_all_repos ?

Copy link
Owner Author

Choose a reason for hiding this comment

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

call is clone now. This clones all the repos.


def clone_command
@clone_command ||= response_body&.map { |info| "git clone #{info['clone_url']}" }&.join(' & ')
end
Copy link
Collaborator

@MichaelDimmitt MichaelDimmitt Sep 18, 2023

Choose a reason for hiding this comment

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

This is complex. (It has a lot of jobs)

To understand this I had to go work backwards from where I know the api request is in the code and move from there to the next method and then to the next function.

info_uri -> response -> response_body ->
This gets me to the first step in this clone command.

I think code is supposed to read in the other direction

aka. some pseudo code
call method does

map or while loop or iterator that does:

get_repos all_pages - just leave this one as a comment instead of having a function.

  1. get_repos_by_page_from_api
  2. clone_repos_api_result

doing this right after each iteration step will let us start cloning repos right away instead of needing to build the whole list.

PER_PAGE = 30.freeze
BASE_URL = 'https://api.github.com'

def self.get_username
Copy link
Owner Author

Choose a reason for hiding this comment

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

Restored the username prompt. This will only run from the clone script if the user didn't provide one in arguments.

end

def clone
validate_clonable
Copy link
Owner Author

Choose a reason for hiding this comment

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

Validate that we have everything before cloning. If something is missing or an API request failed, it will be added to an errors list and printed.

end

def repo_info
@repo_info ||= page_count.times.map { |index| repo_info_for_page(index + 1) }.flatten.compact
Copy link
Owner Author

Choose a reason for hiding this comment

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

Note that this collects all repo info across pages. This all occurs before clone commands are built.

@murjax
Copy link
Owner Author

murjax commented Sep 21, 2023

@MichaelDimmitt Can you re-review for these two questions?

  1. Does this satisfy the specification from issue Roadmap / Application Specification #44 minus stretch?
  2. Is this readable? It's consolidated into a single class right now. There's places to split responsibility into more classes if we want. Will you be able to read it 6 months from now as as?


def final_command
@final_command ||= clone_commands.flatten.join(' & ')
end
Copy link
Collaborator

Choose a reason for hiding this comment

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

I still do not like this "final_command" name
string_to_clone_all_repos_as_bash_jobs 🤣

Copy link
Owner Author

Choose a reason for hiding this comment

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

clone_all_command? Open to easy to read ideas.

elsif user_info&.dig('public_repos').to_i == 0
errors.push('No repositories found at this account')
end
end
Copy link
Collaborator

Choose a reason for hiding this comment

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

Similar to what you wanted me to do on the bash command line

why not just forward the error message sent by the server?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we check http_status codes instead of string matching the api message?

Copy link
Owner Author

Choose a reason for hiding this comment

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

This checks the user first. The response here doesn't have an error. We just know to break here if the repo count is 0. In other words there's no Github server error, it's a local error.

repo_info.each do |info|
if info&.dig('message')&.include?('API rate limit exceeded')
errors.push('API rate limit exceeded')
end
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we do not need this if statement here.

Copy link
Owner Author

Choose a reason for hiding this comment

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

This is iterating through all the repo info responses. Some may have succeeded. I don't want to add an error for successes. Can you elaborate?


def repo_info
@repo_info ||= page_count.times.map do |index|
next if last_repo_info_request_failed
Copy link
Collaborator

@MichaelDimmitt MichaelDimmitt Oct 3, 2023

Choose a reason for hiding this comment

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

next should be break, if the api is violated do not do any more work with the api.

Copy link
Owner Author

Choose a reason for hiding this comment

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

I tried that. The map ends up being nil. Example:

 x = [1,2,3,4]
 y = x.map { |z| z * 2 } # =>  [2, 4, 6, 8]
 y = x.map { |z| z == 2 ? break : z * 2 } # => nil

I might try replacing this with a reduce or manual for loop so I can properly break.

@murjax murjax merged commit d194552 into master Oct 3, 2023
@murjax murjax deleted the ruby_rewrite branch October 3, 2023 22:55
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