Re: [YARD] yard, RubyDoc, style, script, blank lines, singleton_methods

109 views
Skip to first unread message

Loren Segal

unread,
Nov 29, 2015, 10:15:58 PM11/29/15
to yar...@googlegroups.com
Hi Greg,

There's quite a lot of different topics in this single email, but I think the first 3 are all symptoms of the same basic non-YARD-related issue, so I'll answer them all together (style, script, blank lines):

1,2,3: The results of markdown rendering are entirely dependent on the markup provider being used. YARD supports a whole bunch of renderers (via the --markup-provider or -M switch), but rubydoc.info only uses a small subset of those. You can try forcing your provider with -M in your .yardopts file, but if this doesn't work, it's very likely that rubydoc.info does not support your provider. There are a few good reasons for why this is, but specifically, to points 1 and 2, you should not expect rubydoc.info to parse arbitrary JavaScript on a page (long-term). Although this may be possible in certain cases, we are actively reviewing whether we want to officially support this, given the potential for abuse. Similarly, adding custom styles via embedded <style> tags in a markdown file is not a truly appropriate way to customize a template, and is not entirely supported. If you want complete control over the style on a given page, use template customization. Although this is also not currently supported on rubydoc.info, it's likely that this is the way we will expect users to expose these kinds of customizations.

As for issue #3 (newlines), you should *always* use blank lines between paragraphs and lists, or headers and lists, or headers and paragraphs, etc. -- this is not technically prescribed in the Markdown syntax specification (due to the fact that there is no such specification), but not all Markdown libraries handle a missing newline in the same way. This isn't a YARD issue, just an issue with Markdown libraries in general. To ensure that your Markdown is compatible with different providers, you should always add the newline-- all libraries I've tested know how to handle this syntax.

Now, on to the singleton_methods issue:

Throwing a warning for undocumentable code is expected behavior in YARD. It might be valid Ruby, and that's fine, but it's not documentable code. Specifically, the declaration involves dynamic behavior that cannot be easily inferred by static parsing. There are no plans to remove these warnings, as they are telling you that YARD is not picking up documentation for this method, which is useful information for someone documenting an API. You can use -q (or --quiet in long-form) to hide warnings if you don't want to see them, but I would imagine seeing these warnings is a good thing; YARD is telling your API is cannot be fully communicated to your users.

As a sidenote, there are many reasons why you don't want to add singleton methods directly to object instances, but many of these reasons are beyond the scope of documentation, so I won't get into them here. I will however point out that YARD is "mildly opinionated" software, and there are some implied recommendations to follow while using YARD whenever there is a syntax that could adversely documentability. One of YARD's core goals is to improve the documentation quality of Ruby projects. Adding a singleton method to an object instance leads to very poor APIs from a documentation standpoint, and therefore is not in line with YARD's goals. The idea is that YARD should warn you whenever you are doing something that will affect documentation quality. This is a core and strongly held principle in the project.

The recommendation in this case would be to use classes or mixins to document the behavior that you are adding to said object. The following code is functionally equivalent and arguably more maintainable, let alone better documented:

```
module MyBehavior
  def your_method; end
end

obj = Object.new
obj.send(:include, MyBehavior)
obj.your_method
```

Of course, standard inheritance is the easiest here, and, incidentally, also much more maintainable-- and, possibly even more importantly, much more efficient from a performance standpoint.

Hope that helps,

Loren

On 11/29/2015 6:28 PM, MSP-Greg wrote:
To all,

Recently I decided to document approx 260 constants in an API I work with.  I created markdown
templates, and loaded them with Ruby code.  The 260 constants were spread throughout the templates
in tables.  Being an OCD coding type, I decided to have my code create html tables instead of bar ( | )
markdown tables.

## Style Elements in markdown

\[UPDATE\] Now RubyDoc is having issues again.  Showing errors intermittently.

The two below links show the formatting on RubyDoc, and the formatting on GitHub.io, which is using
the html file I generated locally using yard and the same md file.  Scroll up from the link to the
long table and you'll see the difference.



the md file is at --


For these tables, I added a style element as shown below --

```
<style scoped>
#filecontents table.gjl15 { border:none; border-collapse:collapse; margin-bottom:2em;}
#filecontents table.gjl15 thead { border-bottom:2px solid #aaa; background-color:transparent;}
#filecontents table.gjl15 tr    { border:none; background-color:transparent;}
#filecontents table.gjl15 tr:nth-child(5n) { border-bottom:1px solid #bbb;}
#filecontents table.gjl15 th { border:none; padding: 2px 10px 2px 3px; background-color:transparent; text-align:left;}
#filecontents table.gjl15 td { border:none; padding: 2px 10px 2px 3px; background-color:transparent;}
#filecontents table.gjl15 td.c, #filecontents table.gjl15 th.c { text-align:center;}
#filecontents table.gjl15 td.r, #filecontents table.gjl15 th.r { text-align:right;}
#filecontents table.gjl15 td.clr, #filecontents table.gjl15 th.clr { border-bottom:none; width:10em;}
</style>
```

yard added the style element as the first child of the \<div id='filecontents'\> element, which allows it to
work correctly.

RubyDoc wrapped the \<style scoped\>. element in a \<p\> element, which causes it to have no effect.

## Script Elements in markdown

Originally, I added the following script to add the CSS styles I wanted to the last style sheet in the html file.

This worked fine with yard and yard server, but seemed to cause all sorts of issues with RubyDoc.

```
<script>
var ss_last = document.styleSheets.length - 1,
    ss = document.styleSheets[ss_last],
    rules = ss.cssRules,
    rT1 = "#filecontents table.gjl15 ";
ss.insertRule(rT1 + "{ border:none;}", rules.length );
ss.insertRule(rT1 + "thead { border-bottom:2px solid #aaa; background-color:transparent;}", rules.length );
ss.insertRule(rT1 + "tr { border:none; background-color:transparent;}", rules.length );
ss.insertRule(rT1 + "tr:nth-child(5n) { border-bottom:1px solid #aaa;}", rules.length );
ss.insertRule(rT1 + "th { border:none; padding: 2px 10px 2px 3px; background-color:transparent; text-align:left;}", rules.length );
ss.insertRule(rT1 + "td { border:none; padding: 2px 10px 2px 3px; background-color:transparent;}", rules.length );
ss.insertRule(rT1 + "td.c, " + rT1 + "th.c { text-align:center;}", rules.length );
ss.insertRule(rT1 + "td.r, " + rT1 + "th.r { text-align:right;}" , rules.length );
ss.insertRule(rT1 + "td.clr, " + rT1 + "th.clr { border-bottom:none; width:10em;}" , rules.length );
</script>
```

## Blank lines & markdown formatting

The following rendered fine in yard & yard server:

```
Found the following:
* 177 Constants defined in Object (global)
* &nbsp;&nbsp;79 Constants defined in SketchUp objects (namespaced)
* &nbsp;&nbsp;86 SketchUp objects with no defined constants
```

At RubyDoc, it all ran together as one paragraph and needed to be formatted as follows
(additional blank line):

```
Found the following:

* 177 Constants defined in Object (global)
* &nbsp;&nbsp;79 Constants defined in SketchUp objects (namespaced)
* &nbsp;&nbsp;86 SketchUp objects with no defined constants
```

## Ruby singleton methods

Ruby allows one to add methods to plain vanilla fudge objects.

```ruby
obj = Object.new
def obj.your_method
end
```

I found this syntax caused issues possibly everywhere (yard doc, yard server, & RubyDoc).  Some of the
issues went away if, instead of using a simple variable, obj, I used a class variable (@@obj) or instance
variable (@obj).  Better yet, I wrapped all method definitions in a instance_eval block:

```ruby
obj = Object.new
  obj.instance_eval {
    def method1
      #
    end
    def method2
      #
    end
  }
```

Regardless, the first syntax style is valid Ruby, and shouldn't throw so many errors and/or warnings.

Thanks,

Greg
--

---
You received this message because you are subscribed to the Google Groups "YARD" group.
To unsubscribe from this group and stop receiving emails from it, send an email to yardoc+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

MSP-Greg

unread,
Nov 29, 2015, 11:55:51 PM11/29/15
to YARD

Loren,

 

Thanks for the response.

 

Both GitHub and my local .yardopts file have the following line in them –

 

--markup markdown

 

I would agree that <script> tags are probably inappropriate in md, but if the author needs them, I guess I’m undecided.

 

As to <style scoped> tags, I think they allow a great deal of flexibility for control, if required or desired.

 

Hence, it would certainly be helpful if RubyDoc supported them and added them at the root of the md content, unless they are contained in an html tag in the md.  I didn’t want to repeat the

<style> element in every table, so I was happy to see that yard added the single <style scoped> element at the top.

 

As an aside, for those unfamiliar with some particulars of ‘proper’ html, <script> elements are *only* permitted as children of the <head> element.  Conversely, <script scoped> elements can exist anywhere.  But, they essentially only affect the styling of descendants of its parent element.  Hence, RubyDoc’s placing it a <p> element broke my md parsing.

 

As an aside, that’s one thing I haven’t really seen in the CommonMark spec (which supports <style> tags).  It doesn’t seem to clearly define the html generated from md; things like white space between elements, etc.  I need to read the whole thing, but…

 

Re the singleton_methods, thanks for the info.  I abbreviated the example a bit too far, the object is normally an instance of an API class, and its methods are used for callbacks/event sinks for domain environment changes by the UI or other API consumers.  Since Ruby has no connection between inner & outer classes (unlike some other languages), and since these objects are typically singletons and used in or with a mediator type object, singleton_methods are helpful and simpler from my perspective, and don’t require defining another class.

 

Thanks for all your work on yard and RubyDoc.info.  I wish I knew yard better, but I haven’t had the time.  I’d rather propose a fix that you can choose to accept or not.

 

Hence, I’m not saying ‘fix this’; I’m just trying to point out to others some of the things that have caught me up.

 

Greg

Loren Segal

unread,
Nov 30, 2015, 12:34:36 AM11/30/15
to yar...@googlegroups.com
Hey Greg,

Some more responses inline:


On 11/29/2015 8:47 PM, MSP-Greg wrote:

Both GitHub and my local .yardopts file have the following line in them –

 --markup markdown


That is the --markup flag, which is different from the --markup-provider flag. These are different things. The --markup-provider flag selects the library to render the main markup type with. YARD uses Redcarpet by default, but it may not be the provider you are using, and Rubydoc.info specifically uses v1.17.2 due to compat issues (https://github.com/lsegal/rubydoc.info/blob/master/Gemfile#L9). You can try another provider like rdiscount, if you'd like. The full list of supported providers is here:

https://github.com/lsegal/yard/blob/master/lib/yard/templates/helpers/markup_helper.rb#L23

You can cross-reference that with the Gemfile above to find one that works for you.


Hence, it would certainly be helpful if RubyDoc supported them and added them at the root of the md content, unless they are contained in an html tag in the md.  I didn’t want to repeat the


RubyDoc is just YARD-- but more importantly, YARD does not do anything with Markdown content. Markdown is passed off entirely to the underlying markup provider to render HTML, and YARD has no control over how it gets rendered. Again, this is something that is per-provider dependent.


As an aside, for those unfamiliar with some particulars of ‘proper’ html, <script> elements are *only* permitted as children of the <head> element.  Conversely, <script scoped> elements can exist anywhere.  But, they essentially only affect the styling of descendants of its parent element.  Hence, RubyDoc’s placing it a <p> element broke my md parsing.

This, again, is not a YARD issue, but an issue with the markup provider. The markup library is adding the paragraph tag, probably due to whitespace discrepancies. I would suggest trying to force the markup provider to one that you know works the way you'd like, or using proper newline spacing between blocks, as many Markup libraries don't like blocks that are not separated by whitespace. That may be all there is to the issue you're running into. That said, it is not guaranteed that Rubydoc will support style or script tags in the future, and is not something you should really be relying on.

To be honest though, I don't really see a big difference in the tabular styling you're using, but if this is really important, it may be worth having a discussion on the YARD issue tracker about adding a "compact" class to <table> tags in YARD's default template that users could opt into. That way any markup provider that rendered `<table class="compact">` would render closer to what you're looking for. That seems like a better solution to injecting CSS/JS into a Markdown file, and something I'm not opposed to, given that it can be opt-in.


Re the singleton_methods, thanks for the info.  I abbreviated the example a bit too far, the object is normally an instance of an API class, and its methods are used for callbacks/event sinks for domain environment changes by the UI or other API consumers.  Since Ruby has no connection between inner & outer classes (unlike some other languages), and since these objects are typically singletons and used in or with a mediator type object, singleton_methods are helpful and simpler from my perspective, and don’t require defining another class.


The "proper" way to implement a callback is to use an observer/listener pattern, or even delegates, such that you are registering methods, lambdas, or objects to call known methods on as callbacks. That is both more efficient and more documentable (because you can document the insertion points & callback signatures). Whenever I see "doesn't require another class" I read it as "this is secret sauce", which is just a code word for undocumentable magic. It's okay to take shortcuts when you're building things, but it's important to realize that many shortcuts should be called what they are-- "technical debt"-- not "simpler". That said, you don't *need* to use define another class to build a delegate/listener pattern in Ruby-- calling back to lambdas works just as well, and doesn't incur perf costs like busting your method cache for defining new methods on an object instance.

Loren

Loren Segal

unread,
Nov 30, 2015, 12:35:25 AM11/30/15
to yar...@googlegroups.com
I forgot to add that I'm a huge fan of SketchUp, by the way!

- Loren

MSP-Greg

unread,
Dec 2, 2015, 2:00:05 PM12/2/15
to YARD
Loren,

Between some other coding, got back to the style issue.  On RubyDoc.info, many of my style lines began with #filecontents, which (as you know) is the id of the element containing the md info.  I failed to notice that the renderer was adding an <h1> tag to those lines.
Once I escaped them, everything is okay.  It renders fine on RubyDoc, but it breaks my local yard generation.  Thank you for the explanation, as I clearly understand why.

Before I escaped them, the <style> start tag was enclosed in a <p> element. Now that I escaped the inner lines, the <style> element is enclosed in a <p> tag.  Since most browsers do not enforce the html5 requirement that all 'un-scoped' <style> elements appear in the metadata section (<head>), and uses them to style content, I'm fine for a while.

Re the code, it's my understanding that the method cache is 'busted' anytime the 'def' keyword appears.  In this instance, the object would be created while several things are loading, and hence, the cache is being busted until everything loads...

Thanks,

Greg
Reply all
Reply to author
Forward
0 new messages