openhab-jrubyscripting 5.0.0.rc9 → 5.0.0.rc11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/lib/openhab/core/actions/audio.rb +47 -0
  3. data/lib/openhab/core/actions/ephemeris.rb +39 -0
  4. data/lib/openhab/core/actions/exec.rb +51 -0
  5. data/lib/openhab/core/actions/http.rb +80 -0
  6. data/lib/openhab/core/actions/ping.rb +30 -0
  7. data/lib/openhab/core/actions/transformation.rb +32 -0
  8. data/lib/openhab/core/actions/voice.rb +36 -0
  9. data/lib/openhab/core/actions.rb +23 -120
  10. data/lib/openhab/core/{events → dto}/item_channel_link.rb +1 -4
  11. data/lib/openhab/core/{events → dto}/thing.rb +10 -12
  12. data/lib/openhab/core/dto.rb +11 -0
  13. data/lib/openhab/core/entity_lookup.rb +1 -1
  14. data/lib/openhab/core/events/abstract_event.rb +1 -0
  15. data/lib/openhab/core/events/abstract_item_registry_event.rb +36 -0
  16. data/lib/openhab/core/events/abstract_thing_registry_event.rb +40 -0
  17. data/lib/openhab/core/events/item_command_event.rb +1 -1
  18. data/lib/openhab/core/events/item_state_changed_event.rb +6 -6
  19. data/lib/openhab/core/events/item_state_event.rb +6 -6
  20. data/lib/openhab/core/events/thing_status_info_event.rb +8 -6
  21. data/lib/openhab/core/items/generic_item.rb +2 -1
  22. data/lib/openhab/core/items/persistence.rb +52 -18
  23. data/lib/openhab/core/items/player_item.rb +1 -1
  24. data/lib/openhab/core/items/proxy.rb +20 -14
  25. data/lib/openhab/core/items/registry.rb +2 -0
  26. data/lib/openhab/core/items.rb +3 -3
  27. data/lib/openhab/core/profile_factory.rb +3 -1
  28. data/lib/openhab/core/proxy.rb +125 -0
  29. data/lib/openhab/core/things/links/provider.rb +1 -1
  30. data/lib/openhab/core/things/proxy.rb +8 -0
  31. data/lib/openhab/core/types/date_time_type.rb +2 -1
  32. data/lib/openhab/core/types/decimal_type.rb +1 -1
  33. data/lib/openhab/core/types/un_def_type.rb +2 -2
  34. data/lib/openhab/core/value_cache.rb +1 -1
  35. data/lib/openhab/core_ext/ephemeris.rb +53 -0
  36. data/lib/openhab/core_ext/java/class.rb +1 -1
  37. data/lib/openhab/core_ext/java/duration.rb +25 -1
  38. data/lib/openhab/core_ext/java/local_date.rb +2 -0
  39. data/lib/openhab/core_ext/java/month_day.rb +2 -0
  40. data/lib/openhab/core_ext/java/zoned_date_time.rb +85 -0
  41. data/lib/openhab/core_ext/ruby/date.rb +2 -0
  42. data/lib/openhab/core_ext/ruby/date_time.rb +1 -0
  43. data/lib/openhab/core_ext/ruby/time.rb +1 -0
  44. data/lib/openhab/dsl/debouncer.rb +259 -0
  45. data/lib/openhab/dsl/items/builder.rb +4 -2
  46. data/lib/openhab/dsl/items/timed_command.rb +31 -13
  47. data/lib/openhab/dsl/rules/automation_rule.rb +28 -21
  48. data/lib/openhab/dsl/rules/builder.rb +357 -37
  49. data/lib/openhab/dsl/rules/guard.rb +12 -54
  50. data/lib/openhab/dsl/rules/name_inference.rb +11 -0
  51. data/lib/openhab/dsl/rules/property.rb +3 -4
  52. data/lib/openhab/dsl/rules/terse.rb +4 -1
  53. data/lib/openhab/dsl/rules/triggers/conditions/duration.rb +5 -6
  54. data/lib/openhab/dsl/rules/triggers/cron/cron.rb +1 -0
  55. data/lib/openhab/dsl/rules/triggers/cron/cron_handler.rb +19 -31
  56. data/lib/openhab/dsl/rules/triggers/watch/watch.rb +1 -0
  57. data/lib/openhab/dsl/rules/triggers/watch/watch_handler.rb +22 -30
  58. data/lib/openhab/dsl/things/builder.rb +1 -1
  59. data/lib/openhab/dsl/thread_local.rb +1 -0
  60. data/lib/openhab/dsl/version.rb +1 -1
  61. data/lib/openhab/dsl.rb +224 -3
  62. data/lib/openhab/rspec/hooks.rb +5 -2
  63. data/lib/openhab/rspec/karaf.rb +7 -0
  64. data/lib/openhab/rspec/mocks/instance_method_stasher.rb +22 -0
  65. data/lib/openhab/rspec/mocks/space.rb +23 -0
  66. data/lib/openhab/rspec/openhab/core/actions.rb +16 -4
  67. data/lib/openhab/rspec/openhab/core/items/proxy.rb +1 -13
  68. data/lib/openhab/rspec/suspend_rules.rb +1 -14
  69. data/lib/openhab/yard/base_helper.rb +19 -0
  70. data/lib/openhab/yard/code_objects/group_object.rb +9 -3
  71. data/lib/openhab/yard/coderay.rb +17 -0
  72. data/lib/openhab/yard/handlers/jruby/base.rb +10 -1
  73. data/lib/openhab/yard/handlers/jruby/java_import_handler.rb +3 -0
  74. data/lib/openhab/yard/html_helper.rb +49 -15
  75. data/lib/openhab/yard/markdown_helper.rb +135 -0
  76. data/lib/openhab/yard.rb +6 -0
  77. metadata +36 -4
data/lib/openhab/dsl.rb CHANGED
@@ -37,6 +37,13 @@ module OpenHAB
37
37
  public_class_method(*mod.private_instance_methods)
38
38
  end
39
39
 
40
+ class << self
41
+ # @!visibility private
42
+ attr_reader :debouncers
43
+ end
44
+
45
+ @debouncers = java.util.concurrent.ConcurrentHashMap.new
46
+
40
47
  module_function
41
48
 
42
49
  # @!group Rule Creation
@@ -53,6 +60,8 @@ module OpenHAB
53
60
 
54
61
  # @!group Rule Support
55
62
 
63
+ # rubocop:disable Layout/LineLength
64
+
56
65
  #
57
66
  # Defines a new profile that can be applied to item channel links.
58
67
  #
@@ -79,8 +88,8 @@ module OpenHAB
79
88
  # @see org.openhab.thing.Profile
80
89
  # @see org.openhab.thing.StateProfile
81
90
  #
82
- # @example
83
- # profile(:veto_closing_shades) do |event, item:, command: nil|
91
+ # @example Vetoing a command
92
+ # profile(:veto_closing_shades) do |event, item:, command:|
84
93
  # next false if command&.down?
85
94
  #
86
95
  # true
@@ -94,9 +103,37 @@ module OpenHAB
94
103
  # # can also be referenced from an `.items` file:
95
104
  # # Rollershutter MyShade { channel="thing:rollershutter"[profile="ruby:veto_closing_shades"] }
96
105
  #
106
+ # @example Overriding units from a binding
107
+ # profile(:set_uom) do |event, configuration:, state:, command:|
108
+ # unless configuration["unit"]
109
+ # logger.warn("Unit configuration not provided for set_uom profile")
110
+ # next true
111
+ # end
112
+ #
113
+ # case event
114
+ # when :state_from_handler
115
+ # next true unless state.is_a?(DecimalType) || state.is_a?(QuantityType) # what is it then?!
116
+ #
117
+ # state = state.to_d if state.is_a?(QuantityType) # ignore the units if QuantityType was given
118
+ # callback.send_update(state | configuration["unit"])
119
+ # false
120
+ # when :command_from_item
121
+ # # strip the unit from the command, as the binding likely can't handle it
122
+ # next true unless command.is_a?(QuantityType)
123
+ #
124
+ # callback.send_command(DecimalType.new(command.to_d))
125
+ # false
126
+ # else
127
+ # true # pass other events through as normal
128
+ # end
129
+ # end
130
+ # # can also be referenced from an `.items` file:
131
+ # # Number:Temperature MyTempWithNonUnitValueFromBinding "I prefer Celsius [%d °C]" { channel="something_that_returns_F"[profile="ruby:set_uom", unit="°F"] }
132
+ #
97
133
  def profile(id, &block)
98
134
  raise ArgumentError, "Block is required" unless block
99
135
 
136
+ id = id.to_s
100
137
  uid = org.openhab.core.thing.profiles.ProfileTypeUID.new("ruby", id)
101
138
 
102
139
  ThreadLocal.thread_local(openhab_rule_type: "profile", openhab_rule_uid: id) do
@@ -104,6 +141,8 @@ module OpenHAB
104
141
  end
105
142
  end
106
143
 
144
+ # rubocop:enable Layout/LineLength
145
+
107
146
  # @!group Object Access
108
147
 
109
148
  #
@@ -335,6 +374,133 @@ module OpenHAB
335
374
  Range.new(start, finish, range.exclude_end?)
336
375
  end
337
376
 
377
+ #
378
+ # Limits the frequency of calls to the given block within a given amount of time.
379
+ #
380
+ # It can be useful to throttle certain actions even when a rule is triggered too often.
381
+ # This can be used to debounce triggers in a UI rule.
382
+ #
383
+ # @param (see Debouncer#initialize)
384
+ # @param [Object] id ID to associate with this debouncer.
385
+ # @param [Block] block The block to be debounced.
386
+ #
387
+ # @return [void]
388
+ #
389
+ # @example Run at most once per second even when being called more frequently
390
+ # (1..100).each do
391
+ # debounce for: 1.second do
392
+ # logger.info "This will not be logged more frequently than every 1 second"
393
+ # end
394
+ # sleep 0.1
395
+ # end
396
+ #
397
+ # @see Debouncer Debouncer class
398
+ # @see Rules::BuilderDSL#debounce debounce rule guard
399
+ #
400
+ # @!visibility private
401
+ def debounce(for:, leading: false, idle_time: nil, id: nil, &block)
402
+ interval = binding.local_variable_get(:for)
403
+ id ||= block.source_location
404
+ DSL.debouncers.compute(id) do |_key, debouncer|
405
+ debouncer ||= Debouncer.new(for: interval, leading: leading, idle_time: idle_time)
406
+ debouncer.call(&block)
407
+ debouncer
408
+ end
409
+ end
410
+
411
+ #
412
+ # Waits until calls to this method have stopped firing for a period of time
413
+ # before executing the block.
414
+ #
415
+ # This method acts as a guard for the given block to ensure that it doesn't get executed
416
+ # too frequently. The debounce_for method can be called as frequently as possible.
417
+ # The given block, however, will only be executed once the `debounce_time` has passed
418
+ # since the last call to debounce_for.
419
+ #
420
+ # This method can be used from within a UI rule as well as from a file-based rule.
421
+ #
422
+ # @param (see Rules::BuilderDSL#debounce_for)
423
+ # @param [Object] id ID to associate with this call.
424
+ # @param [Block] block The block to be debounced.
425
+ #
426
+ # @return [void]
427
+ #
428
+ # @example Run a block of code only after an item has stopped changing
429
+ # # This can be placed inside a UI rule with an Item Change trigger for
430
+ # # a Door contact sensor.
431
+ # # If the door state has stopped changing state for 10 minutes,
432
+ # # execute the block.
433
+ # debounce_for(10.minutes) do
434
+ # if DoorState.open?
435
+ # Voice.say("The door has been left open!")
436
+ # end
437
+ # end
438
+ #
439
+ # @see Rules::BuilderDSL#debounce_for Rule builder's debounce_for for a detailed description
440
+ #
441
+ def debounce_for(debounce_time, id: nil, &block)
442
+ idle_time = debounce_time.is_a?(Range) ? debounce_time.begin : debounce_time
443
+ debounce(for: debounce_time, idle_time: idle_time, id: id, &block)
444
+ end
445
+
446
+ #
447
+ # Rate-limits block executions by delaying calls and only executing the last
448
+ # call within the given duration.
449
+ #
450
+ # When throttle_for is called, it will hold from executing the block and start
451
+ # a fixed timer for the given duration. Should more calls occur during
452
+ # this time, keep holding and once the wait time is over, execute the block.
453
+ #
454
+ # {throttle_for} will execute the block after it had waited for the given duration,
455
+ # regardless of how frequently `throttle_for` was called.
456
+ # In contrast, {debounce_for} will wait until there is a minimum interval
457
+ # between two triggers.
458
+ #
459
+ # {throttle_for} is ideal in situations where regular status updates need to be made
460
+ # for frequently changing values. It is also useful when a rule responds to triggers
461
+ # from multiple related items that are updated at around the same time. Instead of
462
+ # executing the rule multiple times, {throttle_for} will wait for a pre-set amount
463
+ # of time since the first group of triggers occurred before executing the rule.
464
+ #
465
+ # @param (see Rules::BuilderDSL#throttle_for)
466
+ # @param [Object] id ID to associate with this call.
467
+ # @param [Block] block The block to be throttled.
468
+ #
469
+ # @return [void]
470
+ #
471
+ # @see Rules::BuilderDSL#debounce_for Rule builder's debounce_for for a detailed description
472
+ # @see Rules::BuilderDSL#throttle_for
473
+ #
474
+ def throttle_for(duration, id: nil, &block)
475
+ debounce(for: duration, id: id, &block)
476
+ end
477
+
478
+ #
479
+ # Limit how often the given block executes to the specified interval.
480
+ #
481
+ # {only_every} will execute the given block but prevents further executions
482
+ # until the given interval has passed. In contrast, {throttle_for} will not
483
+ # execute the block immediately, and will wait until the end of the interval.
484
+ #
485
+ # @param (see Rules::BuilderDSL#only_every)
486
+ # @param [Object] id ID to associate with this call.
487
+ # @param [Block] block The block to be throttled.
488
+ #
489
+ # @return [void]
490
+ #
491
+ # @example Prevent door bell from ringing repeatedly
492
+ # # This can be called from a UI rule.
493
+ # # For file based rule, use the `only_every` rule guard
494
+ # only_every(30.seconds) do { Audio.play_sound("doorbell.mp3") }
495
+ #
496
+ # @see Rules::BuilderDSL#debounce_for Rule builder's debounce_for for a detailed description
497
+ # @see Rules::BuilderDSL#only_every
498
+ #
499
+ def only_every(interval, id: nil, &block)
500
+ interval = 1.send(interval) if %i[second minute hour day].include?(interval)
501
+ debounce(for: interval, leading: true, id: id, &block)
502
+ end
503
+
338
504
  #
339
505
  # Store states of supplied items
340
506
  #
@@ -624,7 +790,7 @@ module OpenHAB
624
790
  #
625
791
  # @overload provider!(things: nil, items: nil, metadata: nil, links: nil, **metadata_items)
626
792
  #
627
- # @param [Core::Provider, org.openhab.core.registry.common.ManagedProvider, :persistent, :transient, Proc] providers
793
+ # @param [Core::Provider, org.openhab.core.common.registry.ManagedProvider, :persistent, :transient, Proc] providers
628
794
  # An explicit provider to use. If it's a {Core::Provider}, the type will be inferred automatically.
629
795
  # Otherwise it's applied to all types.
630
796
  # @param [Hash] providers_by_type
@@ -703,6 +869,61 @@ module OpenHAB
703
869
  old_providers
704
870
  end
705
871
 
872
+ #
873
+ # @see CoreExt::Ephemeris
874
+ #
875
+ # @overload holiday_file(file)
876
+ #
877
+ # Sets a thread local variable to use a specific holiday file
878
+ # for {CoreExt::Ephemeris ephemeris calls} inside the block.
879
+ #
880
+ # @param [String, nil] file Path to a file defining holidays;
881
+ # `nil` to reset to default.
882
+ # @yield [] Block executed in context of the supplied holiday file
883
+ # @return [Object] The return value from the block.
884
+ #
885
+ # @example Set a specific holiday configuration file temporarily
886
+ # holiday_file("/home/cody/holidays.xml") do
887
+ # Time.now.next_holiday
888
+ # end
889
+ #
890
+ # @see holiday_file!
891
+ #
892
+ # @overload holiday_file
893
+ #
894
+ # Returns the current thread local value for the holiday file.
895
+ #
896
+ # @return [String, nil] the current holiday file
897
+ #
898
+ def holiday_file(*args)
899
+ raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 0..1)" if args.length > 1
900
+
901
+ old = Thread.current[:openhab_holiday_file]
902
+ return old if args.empty?
903
+
904
+ holiday_file!(args.first)
905
+ yield
906
+ ensure
907
+ holiday_file!(old)
908
+ end
909
+
910
+ #
911
+ # Sets a thread local variable to set the default holiday file.
912
+ #
913
+ # @see https://github.com/svendiedrichsen/jollyday/tree/master/src/main/resources/holidays Example data files from the source
914
+ #
915
+ # @example
916
+ # holiday_file!("/home/cody/holidays.xml")
917
+ # Time.now.next_holiday
918
+ #
919
+ # @param [String, nil] file Path to a file defining holidays;
920
+ # `nil` to reset to default.
921
+ # @return [Symbol, nil] the new holiday file
922
+ #
923
+ def holiday_file!(file = nil)
924
+ Thread.current[:openhab_holiday_file] = file
925
+ end
926
+
706
927
  # @!visibility private
707
928
  def try_parse_time_like(string)
708
929
  return string unless string.is_a?(String)
@@ -32,6 +32,11 @@ module OpenHAB
32
32
  config.include ExampleGroup
33
33
 
34
34
  config.before(:suite) do
35
+ if config.mock_framework.framework_name == :rspec
36
+ require_relative "mocks/instance_method_stasher"
37
+ require_relative "mocks/space"
38
+ end
39
+
35
40
  Helpers.autorequires unless Configuration.private_confdir
36
41
  Helpers.send(:set_up_autoupdates)
37
42
  Helpers.load_transforms
@@ -86,8 +91,6 @@ module OpenHAB
86
91
  end
87
92
 
88
93
  config.after do
89
- Core::Items::Proxy.reset_cache
90
- Core::Things::Proxy.reset_cache
91
94
  @profile_factory.unregister
92
95
  timers.cancel_all
93
96
  # timers and rules have already been canceled, so we can safely just
@@ -280,6 +280,13 @@ module OpenHAB
280
280
  props = cfg.properties || java.util.Hashtable.new
281
281
  props.put("default", "default")
282
282
  cfg.update(props)
283
+
284
+ # configure ephemeris with basic values
285
+ cfg = ca.get_configuration("org.openhab.ephemeris", nil)
286
+ props = cfg.properties || java.util.Hashtable.new
287
+ props.put("country", "us")
288
+ props.put("dayset-weekend", %w[SATURDAY SUNDAY])
289
+ cfg.update(props)
283
290
  end
284
291
  wait_for_service("org.openhab.core.automation.RuleManager") do |re|
285
292
  require_relative "mocks/synchronous_executor"
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module RSpec
5
+ module Mocks
6
+ module InstanceMethodStasher
7
+ ::RSpec::Mocks::InstanceMethodStasher.prepend(self)
8
+
9
+ # Disable "singleton on non-persistent Java type"
10
+ # it doesn't matter during specs
11
+ def initialize(*)
12
+ original_verbose = $VERBOSE
13
+ $VERBOSE = nil
14
+
15
+ super
16
+ ensure
17
+ $VERBOSE = original_verbose
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module RSpec
5
+ module Mocks
6
+ module Space
7
+ ::RSpec::Mocks::Space.prepend(self)
8
+
9
+ #
10
+ # When setting expectations on {Items::Proxy proxies}, set them against the item
11
+ # themselves, so that it will catch calls against `self` from within the instance.
12
+ #
13
+ def proxy_for(object)
14
+ return super unless ::RSpec.current_example&.example_group&.consistent_proxies?
15
+
16
+ object = object.__getobj__ if object.is_a?(Core::Items::Proxy)
17
+
18
+ super
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -12,12 +12,24 @@ module OpenHAB
12
12
  logger.debug("notify: #{msg}")
13
13
  end
14
14
 
15
- def say(text, voice: nil, sink: nil, volume: nil)
16
- logger.debug("say: #{text}")
15
+ class Voice
16
+ class << self
17
+ def say(text, voice: nil, sink: nil, volume: nil)
18
+ logger.debug("say: #{text}")
19
+ end
20
+ end
17
21
  end
18
22
 
19
- def play_sound(filename, sink: nil, volume: nil)
20
- logger.debug("play_sound: #{filename}")
23
+ class Audio
24
+ class << self
25
+ def play_sound(filename, sink: nil, volume: nil)
26
+ logger.debug("play_sound: #{filename}")
27
+ end
28
+
29
+ def play_stream(url, sink: nil)
30
+ logger.debug("play_stream: #{url}")
31
+ end
32
+ end
21
33
  end
22
34
 
23
35
  # rubocop:enable Lint/UnusedMethodArgument
@@ -4,21 +4,9 @@ module OpenHAB
4
4
  module Core
5
5
  module Items
6
6
  class Proxy
7
- @proxies = {}
8
-
9
7
  class << self
10
- # ensure each item only has a single proxy, so that
11
- # expect(item).to receive(:method) works
12
- def new(item)
13
- return super unless defined?(::RSpec) && ::RSpec.current_example&.example_group&.consistent_proxies?
14
-
15
- @proxies.fetch(item.name) do
16
- @proxies[item.name] = super
17
- end
18
- end
19
-
20
8
  def reset_cache
21
- @proxies = {}
9
+ @proxies.clear
22
10
  end
23
11
  end
24
12
  end
@@ -12,20 +12,7 @@ module OpenHAB
12
12
  logger.trace("Skipping execution of #{uid} because rules are suspended.")
13
13
  return
14
14
  end
15
-
16
- # super
17
- ::OpenHAB::DSL::ThreadLocal.thread_local(**@thread_locals) do
18
- logger.trace { "Execute called with mod (#{mod&.to_string}) and inputs (#{inputs.inspect})" }
19
- logger.trace { "Event details #{inputs["event"].inspect}" } if inputs&.key?("event")
20
- trigger_conditions(inputs).process(mod: mod, inputs: inputs) do
21
- event = extract_event(inputs)
22
- process_queue(create_queue(event), mod, event)
23
- end
24
- rescue Exception => e
25
- raise if defined?(::RSpec) && ::RSpec.current_example.example_group.propagate_exceptions?
26
-
27
- @run_context.send(:logger).log_exception(e)
28
- end
15
+ execute!(mod, inputs)
29
16
  end
30
17
  end
31
18
  # private_constant :AutomationRule
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module YARD
5
+ # @!visibility private
6
+ module BaseHelper
7
+ def link_object(obj, title = nil, *)
8
+ ::YARD::Handlers::JRuby::Base.infer_java_class(obj) if obj.is_a?(String)
9
+ obj = ::YARD::Registry.resolve(object, obj, true, true) if obj.is_a?(String)
10
+ if obj.is_a?(::YARD::CodeObjects::Java::Base) && (see = obj.docstring.tag(:see))
11
+ # link to the first see tag
12
+ return linkify(see.name, title&.to_s || see.text)
13
+ end
14
+
15
+ super
16
+ end
17
+ end
18
+ end
19
+ end
@@ -4,13 +4,19 @@ module OpenHAB
4
4
  module YARD
5
5
  module CodeObjects
6
6
  class GroupObject < ::YARD::CodeObjects::Base
7
+ attr_reader :full_name
8
+
9
+ def initialize(namespace, name)
10
+ @full_name = name
11
+ name = name.delete(%(.?"')).tr(" ", "-")
12
+ super
13
+ end
14
+
7
15
  def path
8
16
  "group#{sep}#{super}"
9
17
  end
10
18
 
11
- def to_s
12
- name
13
- end
19
+ alias_method :to_s, :full_name
14
20
  end
15
21
  end
16
22
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "coderay"
4
+
5
+ module OpenHAB
6
+ module YARD
7
+ module CodeRay
8
+ module HtmlHelper
9
+ ::CodeRay::Scanners.list.each do |scanner|
10
+ define_method("html_syntax_highlight_#{scanner}") do |source|
11
+ ::CodeRay.scan(source, scanner).html
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -7,9 +7,18 @@ module YARD
7
7
  class << self
8
8
  def infer_java_class(klass, inferred_type = nil, comments = nil, statement = nil)
9
9
  components = klass.split(".")
10
+ components.pop if components.last == "freeze"
11
+
10
12
  class_first_char = components.last[0]
11
13
  is_field = components.last == components.last.upcase
12
- is_package = !is_field && class_first_char != class_first_char.upcase
14
+ container_first_char = components[-2]&.[](0)
15
+ is_method = container_first_char &&
16
+ class_first_char != class_first_char.upcase &&
17
+ container_first_char == container_first_char.upcase
18
+ is_package = !is_method && !is_field && class_first_char != class_first_char.upcase
19
+
20
+ # methods aren't supported right now
21
+ return if is_method
13
22
 
14
23
  javadocs = YARD::Config.options.dig(:jruby, "javadocs") || {}
15
24
 
@@ -14,6 +14,9 @@ module YARD
14
14
  obj = infer_java_class(klass)
15
15
  next unless obj
16
16
 
17
+ # don't overwrite an already-extant object with the same name
18
+ next if ::YARD::Registry.at("#{namespace.path}::#{obj.simple_name}")
19
+
17
20
  # then we create a new constant in the current namespace
18
21
  register CodeObjects::ConstantObject.new(namespace, obj.simple_name) { |o|
19
22
  o.source = statement
@@ -1,17 +1,62 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "nokogiri"
4
+
3
5
  module OpenHAB
4
6
  module YARD
5
7
  # @!visibility private
6
8
  module HtmlHelper
9
+ def base_url
10
+ if serializer.is_a?(::YARD::Server::DocServerSerializer)
11
+ abs_url(base_path(router.docs_prefix), "")
12
+ else
13
+ u = url_for("")
14
+ u = "#{u}/" if !u.empty? && u[-1] != "/"
15
+ u
16
+ end
17
+ end
18
+
19
+ def url_for_search_index
20
+ if serializer.is_a?(::YARD::Server::DocServerSerializer)
21
+ url_for_list("index")
22
+ else
23
+ url_for("index.json")
24
+ end
25
+ end
26
+
7
27
  def html_markup_markdown(text)
8
- result = super
28
+ result = super(text)
9
29
 
30
+ html = Nokogiri::HTML5.fragment(result)
10
31
  # re-link files in docs/*.md. They're written so they work on github without any
11
32
  # processing
12
- result.gsub!(%r{<a href="(?:[A-Za-z0-9_/-]+/)*([A-Za-z0-9_-]+).md(#[A-Za-z0-9_/-]+)?"},
13
- "<a href=\"file.\\1.html\\2\"")
14
- result
33
+ unless serializer.is_a?(::YARD::Server::DocServerSerializer)
34
+ html.css("a[href!='']").each do |link|
35
+ uri = URI.parse(link["href"])
36
+ next unless uri.relative? && File.extname(uri.path) == ".md"
37
+
38
+ basename = File.basename(uri.path, ".md")
39
+ uri.path = "file.#{basename}.html"
40
+ link["href"] = uri.to_s
41
+ end
42
+ end
43
+
44
+ # wtf commonmarker, you don't generate anchors?!
45
+ html.css("h1, h2, h3, h4, h5, h6").each do |header|
46
+ next if header["id"]
47
+
48
+ id = header.text.strip.downcase.delete(%(.?"')).tr(" ", "-")
49
+ header["id"] = id
50
+ header.prepend_child(%(<a href="##{id}" class="header-anchor">#</a>))
51
+ end
52
+
53
+ html.to_s
54
+ end
55
+
56
+ def link_url(url, title = nil, params = {})
57
+ params.merge!(@link_attrs) if defined?(@link_attrs) && @link_attrs
58
+ params[:class] = params.delete(:classes).join(" ") if params[:classes]
59
+ super
15
60
  end
16
61
 
17
62
  # have to completely replace this method. only change is the regex splitting
@@ -28,17 +73,6 @@ module OpenHAB
28
73
  list.empty? ? "" : (brackets ? "(#{list.join(", ")})" : list.join(", "))
29
74
  end
30
75
  # rubocop:enable Style/NestedTernaryOperator, Style/StringConcatenation, Style/TernaryParentheses
31
-
32
- def link_object(obj, title = nil, *)
33
- ::YARD::Handlers::JRuby::Base.infer_java_class(obj) if obj.is_a?(String)
34
- obj = ::YARD::Registry.resolve(object, obj, true, true) if obj.is_a?(String)
35
- if obj.is_a?(::YARD::CodeObjects::Java::Base) && (see = obj.docstring.tag(:see))
36
- # link to the first see tag
37
- return linkify(see.name, title&.to_s || see.text)
38
- end
39
-
40
- super
41
- end
42
76
  end
43
77
  end
44
78
  end