yard-virtus 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/Gemfile +17 -0
  4. data/Gemfile.lock +75 -0
  5. data/Guardfile +12 -0
  6. data/LICENSE.txt +19 -0
  7. data/README.md +49 -0
  8. data/Rakefile +7 -0
  9. data/example/README.md +4 -0
  10. data/example/address.rb +7 -0
  11. data/example/city.rb +5 -0
  12. data/example/user.rb +9 -0
  13. data/lib/yard-virtus.rb +28 -0
  14. data/lib/yard/virtus/code_objects.rb +2 -0
  15. data/lib/yard/virtus/code_objects/attribute_reader.rb +26 -0
  16. data/lib/yard/virtus/code_objects/attribute_writer.rb +48 -0
  17. data/lib/yard/virtus/declarations.rb +4 -0
  18. data/lib/yard/virtus/declarations/options.rb +43 -0
  19. data/lib/yard/virtus/declarations/type.rb +65 -0
  20. data/lib/yard/virtus/declarations/virtus_attribute.rb +67 -0
  21. data/lib/yard/virtus/declarations/virtus_model.rb +33 -0
  22. data/lib/yard/virtus/handlers.rb +2 -0
  23. data/lib/yard/virtus/handlers/include_virtus_model.rb +32 -0
  24. data/lib/yard/virtus/handlers/virtus_attribute.rb +58 -0
  25. data/lib/yard/virtus/version.rb +5 -0
  26. data/spec/code_objects/attribute_reader_spec.rb +38 -0
  27. data/spec/code_objects/attribute_writer_spec.rb +75 -0
  28. data/spec/declarations/options_spec.rb +50 -0
  29. data/spec/declarations/type_spec.rb +37 -0
  30. data/spec/declarations/virtus_attribute_spec.rb +73 -0
  31. data/spec/declarations/virtus_model_spec.rb +35 -0
  32. data/spec/examples/include_virtus_model_001.rb.txt +15 -0
  33. data/spec/examples/virtus_attribute_001.rb.txt +67 -0
  34. data/spec/handlers/include_virtus_model_spec.rb +22 -0
  35. data/spec/handlers/virtus_attribute_spec.rb +71 -0
  36. data/spec/spec_helper.rb +65 -0
  37. data/spec/support/helpers/handler_helpers.rb +9 -0
  38. data/spec/support/helpers/parsing_helpers.rb +13 -0
  39. data/spec/support/matchers/attribute_matchers.rb +45 -0
  40. data/spec/support/matchers/method_type_matchers.rb +11 -0
  41. data/yard-virtus.gemspec +22 -0
  42. metadata +117 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: 9ffd190d1362aad92b5aa2fcccf2562f228b3447
4
+ data.tar.gz: 57e661c5aeb9d7701b2c20a4523fb4dfee843f64
5
+ SHA512:
6
+ metadata.gz: 36cf0432fe4431772a065fdbe398247f478b49a8edc546859a154efda5c0b38a3a9a153bf1630aea5bab2e1a2773efb185c01b8e5d84621893e1ad04e2d66fed
7
+ data.tar.gz: dbbc324988267db53eb72dc0b9a8d937c76a0c8ab71b1f80b7673a4e5f5e24646adc90e3f01b380c4296c008f6fae04268880856a3978db8fe8d61032ae8bc44
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ # Ignore bundler config and scripts/binaries
2
+ /.bundle
3
+ /.bin
4
+
5
+ # Ignore YARD run-off
6
+ .yardoc/
7
+
8
+ # Ignore automatically generated documentation
9
+ # for sample project
10
+ example/doc
11
+
12
+ .DS_Store
13
+ *~
14
+ *#
15
+ *.o
16
+ *.dylib
17
+ *.a
18
+ *.so
19
+ .#*
20
+ *.rbc
21
+ .rvmrc
22
+ tmp/
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "virtus", "= 1.0.2"
4
+ gem "yard", "= 0.8.7.4"
5
+
6
+ group :development do
7
+ gem "rspec"
8
+
9
+ gem "guard-rspec", "~> 4.2.9", require: false
10
+ gem "terminal-notifier-guard"
11
+
12
+ gem "rake"
13
+ gem "pry"
14
+
15
+ # Github Flavored Markdown support
16
+ gem "redcarpet"
17
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,75 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ axiom-types (0.1.1)
5
+ descendants_tracker (~> 0.0.4)
6
+ ice_nine (~> 0.11.0)
7
+ thread_safe (~> 0.3, >= 0.3.1)
8
+ celluloid (0.15.2)
9
+ timers (~> 1.1.0)
10
+ coderay (1.1.0)
11
+ coercible (1.0.0)
12
+ descendants_tracker (~> 0.0.1)
13
+ descendants_tracker (0.0.4)
14
+ thread_safe (~> 0.3, >= 0.3.1)
15
+ diff-lcs (1.2.5)
16
+ equalizer (0.0.9)
17
+ ffi (1.9.3)
18
+ formatador (0.2.5)
19
+ guard (2.6.1)
20
+ formatador (>= 0.2.4)
21
+ listen (~> 2.7)
22
+ lumberjack (~> 1.0)
23
+ pry (>= 0.9.12)
24
+ thor (>= 0.18.1)
25
+ guard-rspec (4.2.9)
26
+ guard (~> 2.1)
27
+ rspec (>= 2.14, < 4.0)
28
+ ice_nine (0.11.0)
29
+ listen (2.7.5)
30
+ celluloid (>= 0.15.2)
31
+ rb-fsevent (>= 0.9.3)
32
+ rb-inotify (>= 0.9)
33
+ lumberjack (1.0.6)
34
+ method_source (0.8.2)
35
+ pry (0.9.12.6)
36
+ coderay (~> 1.0)
37
+ method_source (~> 0.8)
38
+ slop (~> 3.4)
39
+ rake (10.3.2)
40
+ rb-fsevent (0.9.4)
41
+ rb-inotify (0.9.4)
42
+ ffi (>= 0.5.0)
43
+ redcarpet (3.1.2)
44
+ rspec (2.14.1)
45
+ rspec-core (~> 2.14.0)
46
+ rspec-expectations (~> 2.14.0)
47
+ rspec-mocks (~> 2.14.0)
48
+ rspec-core (2.14.8)
49
+ rspec-expectations (2.14.5)
50
+ diff-lcs (>= 1.1.3, < 2.0)
51
+ rspec-mocks (2.14.6)
52
+ slop (3.5.0)
53
+ terminal-notifier-guard (1.5.3)
54
+ thor (0.19.1)
55
+ thread_safe (0.3.3)
56
+ timers (1.1.0)
57
+ virtus (1.0.2)
58
+ axiom-types (~> 0.1)
59
+ coercible (~> 1.0)
60
+ descendants_tracker (~> 0.0.3)
61
+ equalizer (~> 0.0.9)
62
+ yard (0.8.7.4)
63
+
64
+ PLATFORMS
65
+ ruby
66
+
67
+ DEPENDENCIES
68
+ guard-rspec (~> 4.2.9)
69
+ pry
70
+ rake
71
+ redcarpet
72
+ rspec
73
+ terminal-notifier-guard
74
+ virtus (= 1.0.2)
75
+ yard (= 0.8.7.4)
data/Guardfile ADDED
@@ -0,0 +1,12 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+ notification :emacs
4
+
5
+ guard :rspec, cmd: "bundle exec rspec" do
6
+ watch(%r{^spec/(.+)/.+_spec\.rb$})
7
+
8
+ watch(%r{^lib/yard/virtus/(.+)/(.+)\.rb$}) { |m| "spec/#{m[1]}/#{m[2]}_spec.rb" }
9
+
10
+ watch('spec/spec_helper.rb') { "spec" }
11
+ watch(%r{spec/support/(.+)/*.rb$}) { "spec" }
12
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2014 Dmitry Dzema
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # yard-virtus
2
+
3
+
4
+ This gem helps to generate YARD documentation for classes built using [Virtus](https://github.com/solnic/virtus). It extracts information about attributes, their types and writers so you don't need to specify it manually.
5
+
6
+ This gem depends on exact details of implementation of Virtus so it's locked to a particular version. In the future I wan to adapt versioning scheme for this gem which will reflect supported Virtus version.
7
+
8
+ ## Install
9
+
10
+ Add the gem to your Gemfile (inside development or documentation group):
11
+
12
+ ``` ruby
13
+ gem "yard-virtus", "= 0.0.4"
14
+ ```
15
+
16
+ ## Usage
17
+
18
+ If you use YARD via Rake and `YARD::Rake::YardocTask` or via custom ruby script add
19
+
20
+ ``` ruby
21
+ require "yard-virtus"
22
+ ```
23
+
24
+ If you use YARD command line tool use `--plugin` switch like this
25
+
26
+ ```shell
27
+ yard --plugin virtus
28
+ ```
29
+
30
+ ## Work in Progress
31
+
32
+ This library is still work in progress and is not recommended for production use.
33
+
34
+ ### TODO
35
+
36
+ * Detect if class gets virtus functionality via inheritance (partially done).
37
+ * Attach documentation about various features inherited from Virtus to namespaces.
38
+ * Extract default values of attributes.
39
+
40
+ ## Notice
41
+
42
+ This library uses eval with $SAFE level 3 to evaluate list of options in
43
+ `attribute` declarations. Setting $SAFE to level 3 means it can not do
44
+ destructive operations on your file system but it can damage objects which
45
+ are already in memory.
46
+
47
+ ### Author
48
+
49
+ [Dmitry Dzema](https://github.com/DimaD) ([@dzema](https://twitter.com/dzema))
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require File.join(File.dirname(__FILE__), "lib", "yard-virtus")
2
+
3
+ desc "Build documentation for sample code set"
4
+ YARD::Rake::YardocTask.new("example") do |t|
5
+ t.files = ["example/**/*.rb"]
6
+ t.options = ["--no-private", "--markup-provider=redcarpet", "--markup=markdown", "--output-dir=./example/doc"]
7
+ end
data/example/README.md ADDED
@@ -0,0 +1,4 @@
1
+ # virtus-yardoc example
2
+
3
+ This is a simple project to demonstrate virtus-yard features.
4
+ Code here is devised from main Virtus reame.
@@ -0,0 +1,7 @@
1
+ class Address
2
+ include Virtus.model
3
+
4
+ attribute :street, String
5
+ attribute :zipcode, String
6
+ attribute :city, City
7
+ end
data/example/city.rb ADDED
@@ -0,0 +1,5 @@
1
+ class City
2
+ include Virtus.model
3
+
4
+ attribute :name, String
5
+ end
data/example/user.rb ADDED
@@ -0,0 +1,9 @@
1
+ class User
2
+ include Virtus.model
3
+
4
+ attribute :name, String
5
+ attribute :age, Integer
6
+
7
+ # User can have work address or home address
8
+ attribute :addresses, Array[Address]
9
+ end
@@ -0,0 +1,28 @@
1
+ require "virtus"
2
+ require "yard"
3
+
4
+ _dir = File.dirname(__FILE__)
5
+ require File.join(_dir, "yard", "virtus", "version")
6
+
7
+ require File.join(_dir, "yard", "virtus", "handlers")
8
+ require File.join(_dir, "yard", "virtus", "declarations")
9
+ require File.join(_dir, "yard", "virtus", "code_objects")
10
+
11
+ # According to ruby conventions if library is called
12
+ # `yard-virtus` it should be included via
13
+ #
14
+ # require "yard/virtus"
15
+ #
16
+ # But YARD plugins do not follow this convention and require
17
+ # gem names like `yard-*` and being required by the same name.
18
+ # Hence this confusing filename.
19
+
20
+ # module YARD
21
+ # module Registry
22
+ # def self.register(object)
23
+
24
+ # return if object.is_a?(YARD::CodeObjects::Proxy)
25
+ # thread_local_store[object.path] = object
26
+ # end
27
+ # end
28
+ # end
@@ -0,0 +1,2 @@
1
+ require File.join(File.dirname(__FILE__), "code_objects", "attribute_reader")
2
+ require File.join(File.dirname(__FILE__), "code_objects", "attribute_writer")
@@ -0,0 +1,26 @@
1
+ module YARD
2
+ module Virtus
3
+ module CodeObjects
4
+ class AttributeReader
5
+ attr_reader :attr_name, :type
6
+
7
+ # @param [Symbol] attr name of attribute
8
+ # @param [String] type string representation of type in YARD format
9
+ def initialize(attr, type)
10
+ @attr_name = attr
11
+ @type = type
12
+ end
13
+
14
+ def method_name
15
+ attr_name
16
+ end
17
+
18
+ def yard_method_object(namespace)
19
+ YARD::CodeObjects::MethodObject.new(namespace, method_name, :instance).tap do |mo|
20
+ mo.add_tag YARD::Tags::Library.new.return_tag("[#{type}]")
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,48 @@
1
+ module YARD
2
+ module Virtus
3
+ module CodeObjects
4
+ class AttributeWriter
5
+ attr_reader :attr_name, :type
6
+
7
+ # @param [Symbol] attr name of attribute
8
+ # @param [String] type string representation of type in YARD format
9
+ # @param [Boolean] is_private indicates if writer is part of private API
10
+ def initialize(attr, type, is_private=false)
11
+ @attr_name = attr
12
+ @type = type
13
+ @is_private = is_private
14
+ end
15
+
16
+ def method_name
17
+ :"#{attr_name}="
18
+ end
19
+
20
+ def yard_method_object(namespace)
21
+ YARD::CodeObjects::MethodObject.new(namespace, method_name, :instance).tap do |mo|
22
+ mo.parameters = [["value", default_value]]
23
+ mo.add_tag param_tag("value", type)
24
+ mo.add_tag private_tag if private?
25
+ end
26
+ end
27
+
28
+ protected
29
+
30
+ def default_value
31
+ nil
32
+ end
33
+
34
+ def private?
35
+ !!@is_private
36
+ end
37
+
38
+ def param_tag(param_name, param_type)
39
+ YARD::Tags::Tag.new("param", nil, param_type, param_name)
40
+ end
41
+
42
+ def private_tag
43
+ YARD::Tags::Tag.new("private", "")
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,4 @@
1
+ require File.join(File.dirname(__FILE__), "declarations", "virtus_model")
2
+ require File.join(File.dirname(__FILE__), "declarations", "virtus_attribute")
3
+ require File.join(File.dirname(__FILE__), "declarations", "type")
4
+ require File.join(File.dirname(__FILE__), "declarations", "options")
@@ -0,0 +1,43 @@
1
+ module YARD
2
+ module Virtus
3
+ module Declarations
4
+ # VirtusModel declaration wraps AST which represents
5
+ # options hash.
6
+ class Options
7
+ attr_reader :ast
8
+
9
+ # @params [YARD::Parser::Ruby::AstNode, nil] ast
10
+ def initialize(ast)
11
+ @ast = ast
12
+ @data = if ast.kind_of?(YARD::Parser::Ruby::AstNode)
13
+ safe_eval("{#{ast.source}}")
14
+ else
15
+ {}
16
+ end
17
+ end
18
+
19
+ def [](key)
20
+ data[key]
21
+ end
22
+
23
+ def empty?
24
+ data.empty?
25
+ end
26
+
27
+ protected
28
+ attr_reader :data
29
+
30
+ # It's not the best idea to use eval but
31
+ # interpreting AST tree to search for hash
32
+ # values is not fun either.
33
+ # @todo replace with AST tree interpreter
34
+ def safe_eval(source)
35
+ proc do |src|
36
+ $SAFE = 3;
37
+ eval(src)
38
+ end.call(source.untaint)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,65 @@
1
+ module YARD
2
+ module Virtus
3
+ module Declarations
4
+ # Type declaration wraps AST which represents
5
+ # type of Virtus attribute
6
+ #
7
+ # @example
8
+ # ast = s(:call, s(:var_ref, s(:const, "Virtus")), :".", s(:ident, "model"))
9
+ # d = VirtusModel.new(ast)
10
+ # d.module_proxies_in_ns(ns) # => P("Virtus.model")
11
+ #
12
+ class Type
13
+ attr_reader :ast
14
+
15
+ # @params [YARD::Parser::Ruby::ReferenceNode, YARD::Parser::Ruby::AstNode] ast
16
+ def initialize(ast)
17
+ @ast = ast
18
+ end
19
+
20
+ def yard_type_string
21
+ if association?(ast)
22
+ yard_type_from_association(ast)
23
+ elsif collection?(ast)
24
+ yard_type_from_collection(ast)
25
+ elsif ast.ref?
26
+ yard_type_from_reference(ast)
27
+ elsif !ast[0].nil?
28
+ Type.new(ast[0]).yard_type_string
29
+ else
30
+ nil
31
+ end
32
+ end
33
+
34
+ protected
35
+ def yard_type_from_reference(tree)
36
+ tree.path.join("::")
37
+ end
38
+
39
+ def yard_type_from_association(tree)
40
+ collection = tree[0].jump(:var_ref)
41
+ key, value = extract_kv_from_association(ast[1])
42
+
43
+ "%s{%s => %s}" % [collection, key, value].map { |type| Type.new(type).yard_type_string }
44
+ end
45
+
46
+ def yard_type_from_collection(ast)
47
+ "%s<%s>" % [ast[0], ast[1]].map { |type| Type.new(type).yard_type_string }
48
+ end
49
+
50
+ def association?(tree)
51
+ tree.jump(:assoc) != tree
52
+ end
53
+
54
+ def collection?(tree)
55
+ tree.type == :aref
56
+ end
57
+ def extract_kv_from_association(tree)
58
+ assoc = tree.jump(:assoc)
59
+
60
+ return assoc[0], assoc[1]
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end