| import { Observable, of, from, interval, throwError } from 'rxjs'; |
| import { map, filter, catchError, switchMap, take, tap } from 'rxjs/operators'; |
|
|
| // Mock function to fetch stock prices (simulates API call) |
| const fetchStockPrice = (ticker: string): Observable<number> => { |
| return new Observable<number>((observer) => { |
| const intervalId = setInterval(() => { |
| if (Math.random() < 0.1) { // Simulating an error 10% of the time |
| observer.error(`Error fetching stock price for ${ticker}`); |
| } else { |
| const price = parseFloat((Math.random() * 1000).toFixed(2)); |
| observer.next(price); |
| } |
| }, 1000); |
|
|
| return () => { |
| clearInterval(intervalId); |
| console.log(`Stopped fetching prices for ${ticker}`); |
| }; |
| }); |
| }; |
|
|
| // Example usage: Tracking stock price updates |
| const stockTicker = 'AAPL'; |
| const stockPrice$ = fetchStockPrice(stockTicker).pipe( |
| map(price => ({ ticker: stockTicker, price })), // Transform data |
| filter(data => data.price > 500), // Only keep prices above 500 |
| tap(data => console.log(`Price update:`, data)), // Side effect: Logging |
| catchError(err => { |
| console.error(err); |
| return of({ ticker: stockTicker, price: null }); // Fallback observable |
| }) |
| ); |
|
|
| // Subscribe to the stock price updates |
| const subscription = stockPrice$.subscribe({ |
| next: data => console.log(`Subscriber received:`, data), |
| error: err => console.error(`Subscription error:`, err), |
| complete: () => console.log('Stream complete'), |
| }); |
|
|
| // Automatically unsubscribe after 10 seconds |
| setTimeout(() => { |
| subscription.unsubscribe(); |
| console.log('Unsubscribed from stock price updates.'); |
| }, 10000); |
| --- |
| class EnforceAttrsMeta(type): |
| """ |
| Metaclass that enforces the presence of specific attributes in a class |
| and automatically decorates methods with a logging wrapper. |
| """ |
| |
| required_attributes = ['name', 'version'] |
|
|
| def __new__(cls, name, bases, class_dict): |
| """ |
| Create a new class with enforced attributes and method logging. |
|
|
| :param name: Name of the class being created. |
| :param bases: Tuple of base classes. |
| :param class_dict: Dictionary of attributes and methods of the class. |
| :return: Newly created class object. |
| """ |
| # Ensure required attributes exist |
| for attr in cls.required_attributes: |
| if attr not in class_dict: |
| raise TypeError(f"Class '{name}' is missing required attribute '{attr}'") |
|
|
| # Wrap all methods in a logging decorator |
| for key, value in class_dict.items(): |
| if callable(value): # Check if it's a method |
| class_dict[key] = cls.log_calls(value) |
|
|
| return super().__new__(cls, name, bases, class_dict) |
|
|
| @staticmethod |
| def log_calls(func): |
| """ |
| Decorator that logs method calls and arguments. |
|
|
| :param func: Function to be wrapped. |
| :return: Wrapped function with logging. |
| """ |
| def wrapper(*args, **kwargs): |
| print(f"Calling {func.__name__} with args={args} kwargs={kwargs}") |
| result = func(*args, **kwargs) |
| print(f"{func.__name__} returned {result}") |
| return result |
| return wrapper |
|
|
|
|
| class PluginBase(metaclass=EnforceAttrsMeta): |
| """ |
| Base class for plugins that enforces required attributes and logging. |
| """ |
| name = "BasePlugin" |
| version = "1.0" |
|
|
| def run(self, data): |
| """ |
| Process the input data. |
|
|
| :param data: The data to be processed. |
| :return: Processed result. |
| """ |
| return f"Processed {data}" |
|
|
|
|
| class CustomPlugin(PluginBase): |
| """ |
| Custom plugin that extends PluginBase and adheres to enforced rules. |
| """ |
| name = "CustomPlugin" |
| version = "2.0" |
|
|
| def run(self, data): |
| """ |
| Custom processing logic. |
|
|
| :param data: The data to process. |
| :return: Modified data. |
| """ |
| return f"Custom processing of {data}" |
|
|
|
|
| # Uncommenting the following class definition will raise a TypeError |
| # because 'version' attribute is missing. |
| # class InvalidPlugin(PluginBase): |
| # name = "InvalidPlugin" |
|
|
|
|
| if __name__ == "__main__": |
| # Instantiate and use the plugin |
| plugin = CustomPlugin() |
| print(plugin.run("example data")) |
| --- |
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Click the Box Game</title> |
| <style> |
| body { |
| text-align: center; |
| font-family: Arial, sans-serif; |
| } |
| #game-container { |
| position: relative; |
| width: 300px; |
| height: 300px; |
| margin: 20px auto; |
| border: 2px solid black; |
| overflow: hidden; |
| } |
| #target { |
| width: 50px; |
| height: 50px; |
| background-color: red; |
| position: absolute; |
| cursor: pointer; |
| } |
| </style> |
| </head> |
| <body> |
| <h1>Click the Box!</h1> |
| <p>Score: <span id="score">0</span></p> |
| <div id="game-container"> |
| <div id="target"></div> |
| </div> |
| <script> |
| let score = 0; |
| const target = document.getElementById("target"); |
| const scoreDisplay = document.getElementById("score"); |
| const container = document.getElementById("game-container"); |
| |
| function moveTarget() { |
| const maxX = container.clientWidth - target.clientWidth; |
| const maxY = container.clientHeight - target.clientHeight; |
| target.style.left = Math.random() * maxX + "px"; |
| target.style.top = Math.random() * maxY + "px"; |
| } |
| |
| target.addEventListener("click", function() { |
| score++; |
| scoreDisplay.textContent = score; |
| moveTarget(); |
| }); |
| |
| moveTarget(); |
| </script> |
| </body> |
| </html> |
|
|