From f4b9d3e0eef7993ab0c805362b5160be040b4e0f Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Mon, 29 Jun 2020 15:32:48 +0300 Subject: [PATCH] Split class and instance methods in model Show them separately on Object page, like on Ruby-Doc.org Also sort them by name and cache. --- app/javascript/packs/app/application.scss | 2 +- app/models/ruby_method.rb | 21 +++-- app/models/ruby_object.rb | 10 +++ app/views/objects/_sidebar.html.slim | 8 +- app/views/objects/show.html.slim | 36 ++------- app/views/objects/show/_methods.html.slim | 27 +++++++ test/controllers/objects_controller_test.rb | 12 ++- test/helpers/application_helper_test.rb | 4 +- test/models/ruby_method_test.rb | 6 +- test/models/ruby_object_test.rb | 87 ++++++++++++++++----- test/test_helper.rb | 58 ++++++++------ 11 files changed, 177 insertions(+), 94 deletions(-) create mode 100644 app/views/objects/show/_methods.html.slim diff --git a/app/javascript/packs/app/application.scss b/app/javascript/packs/app/application.scss index 492f2b0c0..6c40a8eb6 100644 --- a/app/javascript/packs/app/application.scss +++ b/app/javascript/packs/app/application.scss @@ -36,7 +36,7 @@ pre { .ruby-operator { color: #5FB3B3; } p { - @apply py-3; + @apply my-3; } .overflow-wrap-break-word { diff --git a/app/models/ruby_method.rb b/app/models/ruby_method.rb index 2b173b81e..0e229c381 100644 --- a/app/models/ruby_method.rb +++ b/app/models/ruby_method.rb @@ -23,12 +23,19 @@ def object_constant body[:object_constant] end + def class_method? + method_type == "class_method" + end + def instance_method? method_type == "instance_method" end - def class_method? - method_type == "class_method" + def type_identifier + if class_method? then "::" + elsif instance_method? then "#" + else raise "Unknown type of method: #{method_type}" + end end def identifier @@ -86,14 +93,4 @@ def to_hash metadata: metadata } end - - private - - def type_identifier - if instance_method? - "#" - elsif class_method? - "::" - end - end end diff --git a/app/models/ruby_object.rb b/app/models/ruby_object.rb index b5970f32d..4ea59673a 100644 --- a/app/models/ruby_object.rb +++ b/app/models/ruby_object.rb @@ -60,6 +60,16 @@ def ruby_methods @ruby_methods ||= body[:methods].collect { |m| RubyMethod.new(m) } end + def ruby_class_methods + @ruby_class_methods ||= + ruby_methods.select(&:class_method?).sort_by(&:name) + end + + def ruby_instance_methods + @ruby_instance_methods ||= + ruby_methods.select(&:instance_method?).sort_by(&:name) + end + def superclass return @superclass if defined?(@superclass) diff --git a/app/views/objects/_sidebar.html.slim b/app/views/objects/_sidebar.html.slim index e4aaebf68..5a2013406 100644 --- a/app/views/objects/_sidebar.html.slim +++ b/app/views/objects/_sidebar.html.slim @@ -15,17 +15,17 @@ div class="hidden lg:block w-1/4" = render "objects/sidebar/section/link", title: included_module.constant, href: object_path(object: included_module.path) - - unless @object.ruby_methods.select(&:class_method?).empty? + - unless @object.ruby_class_methods.empty? = render "objects/sidebar/section", title: 'Class Methods' do ul class="font-mono text-sm" - - @object.ruby_methods.select(&:class_method?).sort_by(&:name).each do |m| + - @object.ruby_class_methods.each do |m| li = render "objects/sidebar/section/link", title: ":: #{m.name}", href: "##{method_anchor(m)}" - - unless @object.ruby_methods.select(&:instance_method?).empty? + - unless @object.ruby_instance_methods.empty? = render "objects/sidebar/section", title: 'Instance Methods' do ul class="font-mono text-sm" - - @object.ruby_methods.select(&:instance_method?).sort_by(&:name).each do |m| + - @object.ruby_instance_methods.each do |m| li = render "objects/sidebar/section/link", title: "# #{m.name}", href: "##{method_anchor(m)}" diff --git a/app/views/objects/show.html.slim b/app/views/objects/show.html.slim index 24591e6d9..1c2cc93fc 100644 --- a/app/views/objects/show.html.slim +++ b/app/views/objects/show.html.slim @@ -5,7 +5,7 @@ div class="max-w-screen-xl mx-auto px-3 md:px-0 lg:flex" = render "objects/sidebar" div class="w-full mt-16 lg:mt-20 lg:w-3/4" - div class="md:p-3 py-3" + div class="m-3" div class="flex flex-wrap" div class="w-full md:w-6/12" h1 class="lg:text-3xl text-xl text-gray-800 dark:text-gray-200 font-semibold" @@ -20,31 +20,9 @@ div class="max-w-screen-xl mx-auto px-3 md:px-0 lg:flex" | Module div class="ruby-documentation" == @object.description - hr - - @object.ruby_methods.sort_by(&:name).each do |m| - div class="md:p-3 py-3 my-3" - a style="display: block; position: relative; top: -80px; visibility: hidden;" id="#{method_anchor m}" - div class="flex flex-wrap" - div class="w-full md:w-10/12" - a href="##{method_anchor(m)}" - - m.call_sequence.each do |seq| - h4 class="lg:text-2xl text-lg text-gray-900 dark:text-gray-200 font-semibold"= seq - - div class="flex md:justify-end w-full md:w-2/12 mt-3 md:mt-0 font-mono" - - if m.instance_method? - span class="px-2 h-6 inline-block rounded bg-gray-200 dark:bg-gray-700 algin-middle cursor-default" title="Instance Method" - | # - - elsif m.class_method? - span class="px-2 h-6 inline-block rounded bg-gray-200 dark:bg-gray-700 algin-middle cursor-default" title="Class Method" - | :: - a class="px-1 ml-2 h-6 inline-block rounded bg-gray-200 dark:bg-gray-700 align-middle hover:bg-gray-300 hover:text-gray-800 dark-hover:bg-gray-900 dark-hover:text-gray-400 hover:fill-current" href="#{github_url m}" target="_blank" rel="noopener" title="View source on Github" - i class="fab fa-github" - div class="ruby-documentation py-1" - - if m.alias? - div - | An alias for #{m.alias_name} - - elsif m.description.empty? - div - | No documentation available - - else - == m.description + - unless @object.ruby_class_methods.empty? + = render "objects/show/methods", + methods: @object.ruby_class_methods, title: "Class Methods" + - unless @object.ruby_instance_methods.empty? + = render "objects/show/methods", + methods: @object.ruby_instance_methods, title: "Instance Methods" diff --git a/app/views/objects/show/_methods.html.slim b/app/views/objects/show/_methods.html.slim new file mode 100644 index 000000000..8c613b47e --- /dev/null +++ b/app/views/objects/show/_methods.html.slim @@ -0,0 +1,27 @@ +div class="md:px-3 my-3" + h3 class="lg:text-2xl text-lg text-gray-900 dark:text-gray-200 font-semibold mt-8" + = title + hr +- methods.each do |m| + div class="md:px-3 my-3" + a style="display: block; position: relative; top: -80px; visibility: hidden;" id="#{method_anchor m}" + div class="flex flex-wrap" + div class="w-full md:w-10/12" + a href="##{method_anchor(m)}" + - m.call_sequence.each do |seq| + h4 class="lg:text-2xl text-lg text-gray-900 dark:text-gray-200 font-semibold"= seq + + div class="flex md:justify-end w-full md:w-2/12 mt-3 md:mt-0 font-mono" + span class="px-2 h-6 inline-block rounded bg-gray-200 dark:bg-gray-700 algin-middle cursor-default" title="#{m.method_type.capitalize} Method" + = m.type_identifier + a class="px-1 ml-2 h-6 inline-block rounded bg-gray-200 dark:bg-gray-700 align-middle hover:bg-gray-300 hover:text-gray-800 dark-hover:bg-gray-900 dark-hover:text-gray-400 hover:fill-current" href="#{github_url m}" target="_blank" rel="noopener" title="View source on Github" + i class="fab fa-github" + div class="ruby-documentation py-1" + - if m.alias? + div + | An alias for #{m.alias_name} + - elsif m.description.empty? + div + | No documentation available + - else + == m.description diff --git a/test/controllers/objects_controller_test.rb b/test/controllers/objects_controller_test.rb index 11778fef4..545a734e2 100644 --- a/test/controllers/objects_controller_test.rb +++ b/test/controllers/objects_controller_test.rb @@ -78,7 +78,7 @@ def setup string_info[:methods] << { name: "foo", description: "

Hello World

", - method_type: "instance_method", + method_type: "class_method", object_constant: "String", superclass: "Object", included_modules: [], @@ -88,6 +88,16 @@ def setup "foo(arg1, arg2)" ] } + string_info[:methods] << { + name: "bar", + description: "

Hello World

", + method_type: "instance_method", + object_constant: "String", + superclass: "Object", + included_modules: [], + source_location: "2.6.4:string.c:L3", + call_sequence: [] + } string = RubyObject.new(string_info) diff --git a/test/helpers/application_helper_test.rb b/test/helpers/application_helper_test.rb index 52fe71488..46d5606b1 100644 --- a/test/helpers/application_helper_test.rb +++ b/test/helpers/application_helper_test.rb @@ -2,7 +2,7 @@ class ApplicationHelperTest < ActionView::TestCase test "github_url" do - method = ruby_object(String).ruby_methods.first - assert_equal github_url(method), "https://github.com/ruby/ruby/blob/v2_6_4/string.c#L76" + method = ruby_object(String).ruby_class_methods.first + assert_equal github_url(method), "https://github.com/ruby/ruby/blob/v2_6_4/string.c#L3" end end diff --git a/test/models/ruby_method_test.rb b/test/models/ruby_method_test.rb index 15e452bef..f7be9ce49 100644 --- a/test/models/ruby_method_test.rb +++ b/test/models/ruby_method_test.rb @@ -23,6 +23,7 @@ def setup } @method = RubyMethod.new(attributes) + @class_method = RubyMethod.new(method_type: "class_method") end test "required attribues" do @@ -38,11 +39,12 @@ def setup test "#instance_method?" do assert_equal @method.instance_method?, true + assert_equal @class_method.instance_method?, false end test "#class_method?" do - method = RubyMethod.new(method_type: "class_method") - assert_equal method.class_method?, true + assert_equal @class_method.class_method?, true + assert_equal @method.class_method?, false end test "#identifier" do diff --git a/test/models/ruby_object_test.rb b/test/models/ruby_object_test.rb index d6299d806..f1aacafa1 100644 --- a/test/models/ruby_object_test.rb +++ b/test/models/ruby_object_test.rb @@ -17,6 +17,19 @@ def setup depth: 1 }, methods: [ + { + name: "new", + description: "

Hello World

", + method_type: "class_method", + object_constant: "String", + source_location: "2.6.4:string.c:L28", + metadata: { + depth: 1 + }, + call_sequence: <<~G + String.new # => "" + G + }, { name: "to_i", description: "

Hello World

", @@ -68,8 +81,23 @@ def setup end test "ruby_methods" do - method = @object.ruby_methods.first - assert_equal method.class, RubyMethod + assert_equal @object.ruby_methods.all? { |m| m.is_a?(RubyMethod) }, true + end + + test "ruby_class_methods" do + assert_equal( + @object.ruby_class_methods.all? { |m| m.is_a?(RubyMethod) }, + true + ) + assert_equal @object.ruby_class_methods.all?(&:class_method?), true + end + + test "ruby_instance_methods" do + assert_equal( + @object.ruby_instance_methods.all? { |m| m.is_a?(RubyMethod) }, + true + ) + assert_equal @object.ruby_instance_methods.all?(&:instance_method?), true end test "#to_hash" do @@ -83,26 +111,45 @@ def setup metadata: { depth: 1 }, - methods: [{ - name: "to_i", - description: "

Hello World

", - type: :method, - autocomplete: "String#to_i", - object_constant: "String", - identifier: "String#to_i", - method_type: "instance_method", - source_location: "2.6.4:string.c:L54", - call_sequence: <<~G, - str.to_i # => 1 - G - alias: { - path: "String.html#to_integer", - name: "to_integer" + methods: [ + { + name: "new", + description: "

Hello World

", + type: :method, + autocomplete: "String::new", + object_constant: "String", + identifier: "String::new", + method_type: "class_method", + source_location: "2.6.4:string.c:L28", + metadata: { + depth: 1 + }, + call_sequence: <<~G, + String.new # => "" + G + alias: {} }, - metadata: { - depth: 1 + { + name: "to_i", + description: "

Hello World

", + type: :method, + autocomplete: "String#to_i", + object_constant: "String", + identifier: "String#to_i", + method_type: "instance_method", + source_location: "2.6.4:string.c:L54", + metadata: { + depth: 1 + }, + call_sequence: <<~G, + str.to_i # => 1 + G + alias: { + path: "String.html#to_integer", + name: "to_integer" + } } - }], + ], name: "String", object_type: "class_object", superclass: "Object", diff --git a/test/test_helper.rb b/test/test_helper.rb index c1468b128..6e4e48939 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -52,29 +52,41 @@ def ruby_object(constant, object_type: :class) metadata: { depth: 1 }, - methods: [{ - name: "empty?", - description: "

Hello World!

", - method_type: "instance_method", - object_constant: constant.to_s, - source_location: "2.6.4:string.c:76", - metadata: { - depth: 1 - }, - call_sequence: [] - }, { - name: "to_i", - description: "

Hello World

", - method_type: "instance_method", - object_constant: constant.to_s, - source_location: "2.6.4:string.c:54", - metadata: { - depth: 1 - }, - call_sequence: [ - "str.to_i # => 1" - ] - }] + methods: [ + { + name: "new", + description: "

Hello World!

", + method_type: "class_method", + object_constant: constant.to_s, + source_location: "2.6.4:string.c:3", + metadata: { + depth: 1 + }, + call_sequence: [] + }, { + name: "empty?", + description: "

Hello World!

", + method_type: "instance_method", + object_constant: constant.to_s, + source_location: "2.6.4:string.c:76", + metadata: { + depth: 1 + }, + call_sequence: [] + }, { + name: "to_i", + description: "

Hello World

", + method_type: "instance_method", + object_constant: constant.to_s, + source_location: "2.6.4:string.c:54", + metadata: { + depth: 1 + }, + call_sequence: [ + "str.to_i # => 1" + ] + } + ] } RubyObject.new(attributes)