diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 30b9997..eefea88 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,11 +13,6 @@ jobs: env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 ports: - 5432 steps: @@ -26,8 +21,16 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} - uses: ruby/setup-ruby@v1 - run: bundle install + - name: Wait until Postgres is ready to accept connections + run: | + apt-get update && apt-get install -f -y postgresql-client + until pg_isready -h postgres -U postgres -d postgres + do + sleep 1 + echo "Trying again" + done - run: | - bundle exec rake db:create db:schema:load + bundle exec rake db:schema:load bundle exec rspec env: RAILS_ENV: test diff --git a/Gemfile b/Gemfile index 85d74c3..478d4be 100644 --- a/Gemfile +++ b/Gemfile @@ -67,4 +67,5 @@ end gem "money" gem 'acts-as-taggable-on' -gem "vns", path: "../vns" \ No newline at end of file +gem "vns", path: "../vns" +gem "rubytree" diff --git a/Gemfile.lock b/Gemfile.lock index 44a43e5..a571f8a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -97,7 +97,7 @@ GEM debug (1.9.2) irb (~> 1.10) reline (>= 0.3.8) - diff-lcs (1.5.0) + diff-lcs (1.5.1) drb (2.2.1) erubi (1.13.0) factory_bot (6.4.6) @@ -106,6 +106,7 @@ GEM factory_bot (~> 6.4) railties (>= 5.0.0) faker (3.1.1) + faker (3.4.2) i18n (>= 1.8.11, < 2) globalid (1.2.1) activesupport (>= 6.1) @@ -122,6 +123,7 @@ GEM jbuilder (2.12.0) actionview (>= 5.0.0) activesupport (>= 5.0.0) + json (2.7.2) loofah (2.22.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) @@ -134,7 +136,7 @@ GEM method_source (1.0.0) mini_mime (1.1.5) minitest (5.24.1) - money (6.16.0) + money (6.19.0) i18n (>= 0.6.4, <= 2) msgpack (1.7.2) mutex_m (0.2.0) @@ -148,19 +150,19 @@ GEM net-smtp (0.5.0) net-protocol nio4r (2.7.3) - nokogiri (1.16.6-aarch64-linux) + nokogiri (1.16.7-aarch64-linux) racc (~> 1.4) - nokogiri (1.16.6-arm-linux) + nokogiri (1.16.7-arm-linux) racc (~> 1.4) - nokogiri (1.16.6-arm64-darwin) + nokogiri (1.16.7-arm64-darwin) racc (~> 1.4) - nokogiri (1.16.6-x86-linux) + nokogiri (1.16.7-x86-linux) racc (~> 1.4) - nokogiri (1.16.6-x86_64-darwin) + nokogiri (1.16.7-x86_64-darwin) racc (~> 1.4) - nokogiri (1.16.6-x86_64-linux) + nokogiri (1.16.7-x86_64-linux) racc (~> 1.4) - pg (1.5.6) + pg (1.5.7) pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) @@ -168,7 +170,7 @@ GEM stringio puma (6.4.2) nio4r (~> 2.0) - racc (1.8.0) + racc (1.8.1) rack (3.1.7) rack-session (2.0.0) rack (>= 3.0.0) @@ -215,15 +217,15 @@ GEM connection_pool reline (0.5.9) io-console (~> 0.5) - rspec-core (3.12.2) + rspec-core (3.12.3) rspec-support (~> 3.12.0) - rspec-expectations (3.12.3) + rspec-expectations (3.12.4) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) - rspec-mocks (3.12.6) + rspec-mocks (3.12.7) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) - rspec-rails (6.1.0) + rspec-rails (6.1.1) actionpack (>= 6.1) activesupport (>= 6.1) railties (>= 6.1) @@ -231,7 +233,9 @@ GEM rspec-expectations (~> 3.12) rspec-mocks (~> 3.12) rspec-support (~> 3.12) - rspec-support (3.12.1) + rspec-support (3.12.2) + rubytree (2.0.3) + json (~> 2.0, > 2.3.1) sprockets (4.2.1) concurrent-ruby (~> 1.0) rack (>= 2.2.4, < 4) @@ -259,7 +263,7 @@ GEM websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) - zeitwerk (2.6.16) + zeitwerk (2.6.17) PLATFORMS aarch64-linux @@ -284,6 +288,7 @@ DEPENDENCIES rails (~> 7.1.3, >= 7.1.3.2) redis (>= 4.0.1) rspec-rails (~> 6.1.0) + rubytree sprockets-rails stimulus-rails turbo-rails diff --git a/app/extensions/tree_node_extension.rb b/app/extensions/tree_node_extension.rb new file mode 100644 index 0000000..fe4f720 --- /dev/null +++ b/app/extensions/tree_node_extension.rb @@ -0,0 +1,18 @@ +module TreeNodeExtension + def distance_to_common_ancestor(another_node) + return 0 if self == another_node + + my_path = path_as_array + another_path = another_node.path_as_array + + common_elements = my_path.zip(another_path) + .take_while { |(p1, p2)| p1 == p2 } + .count + + return nil if common_elements.zero? + + [my_path.count, another_path.count].max - common_elements + end +end + +Tree::TreeNode.include(TreeNodeExtension) diff --git a/config/environments/development.rb b/config/environments/development.rb index 2e7fb48..794f390 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -9,7 +9,7 @@ Rails.application.configure do config.enable_reloading = true # Do not eager load code on boot. - config.eager_load = false + config.eager_load = true # Show full error reports. config.consider_all_requests_local = true diff --git a/config/environments/test.rb b/config/environments/test.rb index adbb4a6..3ada93b 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -15,7 +15,7 @@ Rails.application.configure do # this is usually not necessary, and can slow down your test suite. However, it's # recommended that you enable it in continuous integration systems to ensure eager # loading is working properly before deploying your code. - config.eager_load = ENV["CI"].present? + config.eager_load = true # Configure public file server for tests with Cache-Control for performance. config.public_file_server.enabled = true diff --git a/spec/extensions/tree_spec.rb b/spec/extensions/tree_spec.rb new file mode 100644 index 0000000..cbefba4 --- /dev/null +++ b/spec/extensions/tree_spec.rb @@ -0,0 +1,71 @@ +require 'rails_helper' + +module Tree + RSpec.describe TreeNode do + describe '#distance_to_common_ancestor' do + def assert_distance(node_1, node_2, distance) + aggregate_failures do + expect(node_1.distance_to_common_ancestor(node_2)).to eq(distance) + expect(node_2.distance_to_common_ancestor(node_1)).to eq(distance) + end + end + context 'when the two nodes are the same' do + it 'returns 0 when comparing the root itself' do + root = Tree::TreeNode.new('root') + assert_distance(root, root, 0) + end + + it 'returns 0 when comparing a child to itself' do + root = Tree::TreeNode.new('root') + child = root << Tree::TreeNode.new('child') + assert_distance(child, child, 0) + end + end + + context 'when the two nodes are siblings' do + it 'returns 1 when comparing siblings' do + root = Tree::TreeNode.new('root') + child1 = root << Tree::TreeNode.new('child1') + child2 = root << Tree::TreeNode.new('child2') + assert_distance(child1, child2, 1) + end + end + + context 'when one node is parent of the other' do + it 'returns 1 when comparing parent to child' do + root = Tree::TreeNode.new('root') + child = root << Tree::TreeNode.new('child') + assert_distance(root, child, 1) + end + end + + context 'when one node is grandparent of the other' do + it 'returns 2 when comparing grandparent to grandchild' do + root = Tree::TreeNode.new('root') + child = root << Tree::TreeNode.new('child') + grandchild = child << Tree::TreeNode.new('grandchild') + assert_distance(root, grandchild, 2) + end + end + + context 'when the two nodes are cousins' do + it 'returns 2 when comparing cousins' do + root = Tree::TreeNode.new('root') + child1 = root << Tree::TreeNode.new('child1') + child2 = root << Tree::TreeNode.new('child2') + grandchild1 = child1 << Tree::TreeNode.new('grandchild1') + grandchild2 = child2 << Tree::TreeNode.new('grandchild2') + assert_distance(grandchild1, grandchild2, 2) + end + end + + context 'when the two nodes are not related' do + it 'returns nil' do + root = Tree::TreeNode.new('root') + another_root = Tree::TreeNode.new('another_root') + assert_distance(root, another_root, nil) + end + end + end + end +end