openhab-jrubyscripting 5.0.0.rc10 → 5.0.0.rc11

Sign up to get free protection for your applications and to get access to all the features.
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