nse_data 0.1.0

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 (52) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +7 -0
  3. data/CHANGELOG.md +22 -0
  4. data/CODE_OF_CONDUCT.md +84 -0
  5. data/Gemfile +24 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +179 -0
  8. data/Rakefile +72 -0
  9. data/doc/NseData/API/Base.html +350 -0
  10. data/doc/NseData/API/SpecialPreopen.html +220 -0
  11. data/doc/NseData/API.html +117 -0
  12. data/doc/NseData/APIManager.html +644 -0
  13. data/doc/NseData/Cache/CachePolicy.html +907 -0
  14. data/doc/NseData/Cache/CacheStore.html +674 -0
  15. data/doc/NseData/Cache.html +117 -0
  16. data/doc/NseData/Client.html +385 -0
  17. data/doc/NseData/Config/Base.html +552 -0
  18. data/doc/NseData/Config/Logger.html +430 -0
  19. data/doc/NseData/Config.html +117 -0
  20. data/doc/NseData/Error.html +124 -0
  21. data/doc/NseData/HttpClient/BaseClient.html +349 -0
  22. data/doc/NseData/HttpClient/FaradayClient.html +301 -0
  23. data/doc/NseData/HttpClient.html +117 -0
  24. data/doc/NseData.html +524 -0
  25. data/doc/_index.html +243 -0
  26. data/doc/class_list.html +54 -0
  27. data/doc/css/common.css +1 -0
  28. data/doc/css/full_list.css +58 -0
  29. data/doc/css/style.css +503 -0
  30. data/doc/file.README.html +304 -0
  31. data/doc/file_list.html +59 -0
  32. data/doc/frames.html +22 -0
  33. data/doc/index.html +304 -0
  34. data/doc/js/app.js +344 -0
  35. data/doc/js/full_list.js +242 -0
  36. data/doc/js/jquery.js +4 -0
  37. data/doc/method_list.html +294 -0
  38. data/doc/top-level-namespace.html +110 -0
  39. data/lib/nse_data/api_manager.rb +83 -0
  40. data/lib/nse_data/cache/README.md +77 -0
  41. data/lib/nse_data/cache/cache_policy.rb +118 -0
  42. data/lib/nse_data/cache/cache_store.rb +84 -0
  43. data/lib/nse_data/cache/redis_cache_store.rb +3 -0
  44. data/lib/nse_data/config/api_endpoints.yml +56 -0
  45. data/lib/nse_data/config/base.rb +38 -0
  46. data/lib/nse_data/config/logger.rb +39 -0
  47. data/lib/nse_data/http_client/base_client.rb +26 -0
  48. data/lib/nse_data/http_client/faraday_client.rb +68 -0
  49. data/lib/nse_data/version.rb +5 -0
  50. data/lib/nse_data.rb +65 -0
  51. data/nse_data.gemspec +34 -0
  52. metadata +131 -0
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NseData
4
+ module Cache
5
+ # CacheStore class provides an in-memory caching mechanism.
6
+ class CacheStore
7
+ def initialize
8
+ @store = {}
9
+ end
10
+
11
+ # Retrieves the cached data for the given key, or fetches fresh data if not cached or expired.
12
+ #
13
+ # @param key [String] The cache key.
14
+ # @param ttl [Integer] The time-to-live in seconds.
15
+ # @yield Fetches fresh data if cache is expired or not present.
16
+ # @return [Object] The cached data or the result of the block if not cached or expired.
17
+ def fetch(key, ttl)
18
+ if cached?(key, ttl)
19
+ @store[key][:data]
20
+ else
21
+ fresh_data = yield
22
+ store(key, fresh_data, ttl)
23
+ fresh_data
24
+ end
25
+ end
26
+
27
+ # Reads data from the cache.
28
+ #
29
+ # @param key [String] The cache key.
30
+ # @return [Object, nil] The cached data, or nil if not present.
31
+ def read(key)
32
+ cached?(key) ? @store[key][:data] : nil
33
+ end
34
+
35
+ # Writes data to the cache with an expiration time.
36
+ #
37
+ # @param key [String] The cache key.
38
+ # @param data [Object] The data to cache.
39
+ # @param ttl [Integer] The time-to-live in seconds.
40
+ def write(key, data, ttl)
41
+ store(key, data, ttl)
42
+ end
43
+
44
+ # Deletes data from the cache.
45
+ #
46
+ # @param key [String] The cache key.
47
+ def delete(key)
48
+ @store.delete(key)
49
+ end
50
+
51
+ private
52
+
53
+ # Checks if the data for the given key is cached and not expired.
54
+ #
55
+ # @param key [String] The cache key.
56
+ # @param ttl [Integer] The time-to-live in seconds.
57
+ # @return [Boolean] Whether the data is cached and valid.
58
+ def cached?(key, ttl = nil)
59
+ return false unless @store.key?(key)
60
+
61
+ !expired?(key, ttl)
62
+ end
63
+
64
+ # Checks if the cached data for the given key has expired.
65
+ #
66
+ # @param key [String] The cache key.
67
+ # @param ttl [Integer] The time-to-live in seconds.
68
+ # @return [Boolean] Whether the cached data has expired.
69
+ def expired?(key, ttl)
70
+ stored_time = @store[key][:timestamp]
71
+ ttl && (Time.now - stored_time) >= ttl
72
+ end
73
+
74
+ # Stores the data in the cache.
75
+ #
76
+ # @param key [String] The cache key.
77
+ # @param data [Object] The data to cache.
78
+ # @param ttl [Integer] The time-to-live in seconds.
79
+ def store(key, data, ttl)
80
+ @store[key] = { data:, timestamp: Time.now, ttl: }
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ # TODO: Implement in near future :)
@@ -0,0 +1,56 @@
1
+ apis:
2
+ all_indices:
3
+ path: "allIndices"
4
+ description: "Fetches data of all available indices."
5
+
6
+ circulars:
7
+ path: "circulars"
8
+ description: "Provides a list of circulars."
9
+
10
+ equity_master:
11
+ path: "equity-master"
12
+ description: "Fetches equity master data."
13
+
14
+ glossary:
15
+ path: "cmsContent?url=/glossary"
16
+ description: "Fetches glossary content."
17
+
18
+ holiday_clearing:
19
+ path: "holiday-master?type=clearing"
20
+ description: "Fetches clearing holiday data."
21
+
22
+ holiday_trading:
23
+ path: "holiday-master?type=trading"
24
+ description: "Fetches trading holiday data."
25
+
26
+ index_names:
27
+ path: "index-names"
28
+ description: "Fetches names of all indices."
29
+
30
+ latest_circulars:
31
+ path: "latest-circular"
32
+ description: "Provides the latest circulars."
33
+
34
+ market_data_pre_open:
35
+ path: "market-data-pre-open?key=ALL"
36
+ description: "Fetches market pre-open data for all securities."
37
+
38
+ market_status:
39
+ path: "marketStatus"
40
+ description: "Fetches the current status of the market."
41
+
42
+ market_turnover:
43
+ path: "market-turnover"
44
+ description: "Fetches turnover data for the market."
45
+
46
+ merged_daily_reports_capital:
47
+ path: "merged-daily-reports?key=favCapital"
48
+ description: "Fetches merged daily reports for capital."
49
+
50
+ merged_daily_reports_debt:
51
+ path: "merged-daily-reports?key=favDebt"
52
+ description: "Fetches merged daily reports for debt."
53
+
54
+ merged_daily_reports_derivatives:
55
+ path: "merged-daily-reports?key=favDerivatives"
56
+ description: "Fetches merged daily reports for derivatives."
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NseData
4
+ module Config
5
+ # Base serves as the base class for configuration settings.
6
+ #
7
+ # It allows for storing and accessing various configuration settings
8
+ # through a hash-like interface.
9
+ #
10
+ # @attr_reader [Hash] settings The hash storing configuration settings.
11
+ class Base
12
+ attr_reader :settings
13
+
14
+ # Initializes the Base with optional initial settings.
15
+ #
16
+ # @param initial_settings [Hash] Optional hash of initial settings.
17
+ def initialize(initial_settings = {})
18
+ @settings = initial_settings
19
+ end
20
+
21
+ # Retrieves a configuration setting by key.
22
+ #
23
+ # @param key [Symbol, String] The key for the setting.
24
+ # @return [Object] The value associated with the key.
25
+ def [](key)
26
+ @settings[key]
27
+ end
28
+
29
+ # Sets a configuration setting by key.
30
+ #
31
+ # @param key [Symbol, String] The key for the setting.
32
+ # @param value [Object] The value to set for the key.
33
+ def []=(key, value)
34
+ @settings[key] = value
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+ require 'tempfile'
5
+ require_relative 'base'
6
+
7
+ module NseData
8
+ module Config
9
+ # Logger handles configuration specifically for logging.
10
+ #
11
+ # It inherits from Base to utilize the hash-like configuration
12
+ # interface and provides default settings for logging.
13
+ #
14
+ # @attr_reader [Logger] logger The Logger instance configured by this class.
15
+ class Logger < Base
16
+ # Creates a default temporary log file.
17
+ def self.default_log_file
18
+ return $stdout if ENV['NSE_DEV']
19
+
20
+ temp_file = Tempfile.new(['nse_data', '.log'])
21
+ temp_file.close # Close the file immediately after creation
22
+ temp_file.path # Return the file path for the Logger
23
+ end
24
+
25
+ # Initializes the Logger with a default or custom Logger instance.
26
+ #
27
+ # @param logger [Logger] Optional custom Logger instance.
28
+ def initialize(logger = ::Logger.new(self.class.default_log_file))
29
+ super()
30
+ @logger = logger
31
+ @settings[:logger] = logger
32
+ # puts "Log file created at: #{self.class.default_log_file}"
33
+ end
34
+
35
+ # Retrieves/Sets the Logger instance.
36
+ attr_accessor :logger
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NseData
4
+ module HttpClient
5
+ # Base class for HTTP clients
6
+ class BaseClient
7
+ # Initializes a new instance of the BaseClient class.
8
+ #
9
+ # @param base_url [String] The base URL for the HTTP client.
10
+ def initialize(base_url, cache_policy)
11
+ @base_url = base_url
12
+ @cache_policy = cache_policy
13
+ end
14
+
15
+ # Sends a GET request to the specified endpoint.
16
+ #
17
+ # @param endpoint [String] The endpoint to send the GET request to.
18
+ # @raise [NotImplementedError] If the method is not implemented by subclasses.
19
+ def get(endpoint)
20
+ raise NotImplementedError, 'Subclasses must implement the get method'
21
+ end
22
+
23
+ # TODO: Other HTTP methods like post, put, delete can be added here if needed
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'faraday-http-cache'
5
+ require 'json'
6
+ require_relative 'base_client'
7
+
8
+ module NseData
9
+ module HttpClient
10
+ # FaradayClient class is responsible for making HTTP requests using Faraday.
11
+ class FaradayClient < BaseClient
12
+ # Sends a GET request to the specified endpoint.
13
+ #
14
+ # @param endpoint [String] The endpoint to send the request to.
15
+ # @param force_refresh [Boolean] Whether to force a cache refresh.
16
+ # @return [Faraday::Response] The response object.
17
+ def get(endpoint, force_refresh: false)
18
+ NseData.logger.debug("#{self.class}##{__method__}: invoking the endpoint #{endpoint}")
19
+ # Use the cache policy to determine whether to fetch from cache or refresh.
20
+ @cache_policy.fetch(endpoint, force_refresh:) do
21
+ handle_connection(endpoint) do |connection|
22
+ connection.get(endpoint)
23
+ end
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ # Handles the connection to the base URL.
30
+ #
31
+ # @yield [connection] The Faraday connection object.
32
+ # @yieldparam connection [Faraday::Connection] The Faraday connection object.
33
+ # @return [Object] The result of the block execution.
34
+ # @raise [Faraday::Error] If there is an error with the connection.
35
+ def handle_connection(endpoint)
36
+ connection = build_faraday_connection(endpoint)
37
+ yield(connection)
38
+ rescue Faraday::Error => e
39
+ NseData.logger.error("#{self.class}##{__method__}: exception is raised - #{e.message}")
40
+ handle_faraday_error(e)
41
+ end
42
+
43
+ def build_faraday_connection(endpoint)
44
+ Faraday.new(url: @base_url) do |faraday|
45
+ configure_faraday(faraday)
46
+ apply_cache_policy(faraday, endpoint) if @cache_policy.use_cache?(endpoint)
47
+ faraday.adapter Faraday.default_adapter
48
+ end
49
+ end
50
+
51
+ def configure_faraday(faraday)
52
+ faraday.request :json
53
+ faraday.headers['User-Agent'] = 'NSEDataClient/1.0'
54
+ faraday.response :json
55
+ faraday.headers['Accept'] = 'application/json'
56
+ end
57
+
58
+ def apply_cache_policy(faraday, endpoint)
59
+ ttl = @cache_policy.ttl_for(endpoint)
60
+ faraday.use :http_cache, store: @cache_policy.cache_store, expire_after: ttl
61
+ end
62
+
63
+ def handle_faraday_error(error)
64
+ raise error.message
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NseData
4
+ VERSION = '0.1.0'
5
+ end
data/lib/nse_data.rb ADDED
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'nse_data/version'
4
+ require_relative 'nse_data/api_manager'
5
+ require_relative 'nse_data/config/logger'
6
+
7
+ # The NseData module serves as the namespace for the NSE Data gem,
8
+ # which provides an interface to interact with and retrieve stock market data
9
+ # from the National Stock Exchange of India.
10
+ module NseData
11
+ class Error < StandardError; end
12
+
13
+ class << self
14
+ # This module provides functionality for accessing NSE data.
15
+ def define_api_methods
16
+ api_manager = APIManager.new
17
+ api_manager.endpoints.each_key do |method_name|
18
+ define_singleton_method("fetch_#{method_name}") do
19
+ api_manager.fetch_data(method_name).body
20
+ end
21
+ end
22
+ end
23
+
24
+ # Returns a list of all available endpoints.
25
+ #
26
+ # @return [Array] An array of endpoint names.
27
+ def list_all_endpoints
28
+ @list_all_endpoints ||= APIManager.new.load_endpoints
29
+ end
30
+
31
+ # Configure the logger for the NseData gem.
32
+ #
33
+ # This method allows users to customize the logger used throughout the library.
34
+ # To use it, call `NseData.configure` and provide a block to set up the logger.
35
+ #
36
+ # Example:
37
+ #
38
+ # NseData.configure do |config|
39
+ # custom_logger = Logger.new('nse_data.log')
40
+ # custom_logger.level = Logger::DEBUG
41
+ # config.logger = custom_logger
42
+ # end
43
+ #
44
+ # @yieldparam [NseData::Config::Logger] config The configuration object to be customized.
45
+ def configure
46
+ @logger_config ||= Config::Logger.new
47
+ yield(@logger_config) if block_given?
48
+ end
49
+
50
+ # Access the configured logger.
51
+ #
52
+ # This method returns the Logger instance configured through `NseData.configure`.
53
+ #
54
+ # @return [Logger] The configured Logger instance.
55
+ # @raise [RuntimeError] If the logger has not been configured.
56
+ def logger
57
+ @logger_config&.logger || (raise 'Logger not configured. Please call NseData.configure first.')
58
+ end
59
+ end
60
+
61
+ # Initialize configuration with default settings.
62
+ @logger_config = Config::Logger.new
63
+ end
64
+
65
+ NseData.define_api_methods
data/nse_data.gemspec ADDED
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/nse_data/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'nse_data'
7
+ spec.version = NseData::VERSION
8
+ spec.authors = ['Sonu Saha']
9
+ spec.email = ['ahasunos@gmail.com']
10
+
11
+ spec.summary = 'A Ruby gem for accessing NSE data'
12
+ spec.description = 'Retrieves stock market data from the NSE (National Stock Exchange) of India'
13
+ spec.homepage = 'https://github.com/ahasunos/nse_data'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = '>= 3.1.0'
16
+
17
+ spec.metadata['homepage_uri'] = spec.homepage
18
+ spec.metadata['source_code_uri'] = 'https://github.com/ahasunos/nse_data'
19
+ spec.metadata['changelog_uri'] = 'https://github.com/ahasunos/nse_data/blob/main/CHANGELOG.md'
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(__dir__) do
24
+ `git ls-files -z`.split("\x0").reject do |f|
25
+ (File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor])
26
+ end
27
+ end
28
+ spec.bindir = 'exe'
29
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ['lib']
31
+
32
+ spec.add_dependency 'faraday', '~> 2.11'
33
+ spec.add_dependency 'faraday-http-cache', '~> 2.5', '>= 2.5.1'
34
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nse_data
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sonu Saha
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-09-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.11'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday-http-cache
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.5'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 2.5.1
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '2.5'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 2.5.1
47
+ description: Retrieves stock market data from the NSE (National Stock Exchange) of
48
+ India
49
+ email:
50
+ - ahasunos@gmail.com
51
+ executables: []
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - ".rubocop.yml"
56
+ - CHANGELOG.md
57
+ - CODE_OF_CONDUCT.md
58
+ - Gemfile
59
+ - LICENSE.txt
60
+ - README.md
61
+ - Rakefile
62
+ - doc/NseData.html
63
+ - doc/NseData/API.html
64
+ - doc/NseData/API/Base.html
65
+ - doc/NseData/API/SpecialPreopen.html
66
+ - doc/NseData/APIManager.html
67
+ - doc/NseData/Cache.html
68
+ - doc/NseData/Cache/CachePolicy.html
69
+ - doc/NseData/Cache/CacheStore.html
70
+ - doc/NseData/Client.html
71
+ - doc/NseData/Config.html
72
+ - doc/NseData/Config/Base.html
73
+ - doc/NseData/Config/Logger.html
74
+ - doc/NseData/Error.html
75
+ - doc/NseData/HttpClient.html
76
+ - doc/NseData/HttpClient/BaseClient.html
77
+ - doc/NseData/HttpClient/FaradayClient.html
78
+ - doc/_index.html
79
+ - doc/class_list.html
80
+ - doc/css/common.css
81
+ - doc/css/full_list.css
82
+ - doc/css/style.css
83
+ - doc/file.README.html
84
+ - doc/file_list.html
85
+ - doc/frames.html
86
+ - doc/index.html
87
+ - doc/js/app.js
88
+ - doc/js/full_list.js
89
+ - doc/js/jquery.js
90
+ - doc/method_list.html
91
+ - doc/top-level-namespace.html
92
+ - lib/nse_data.rb
93
+ - lib/nse_data/api_manager.rb
94
+ - lib/nse_data/cache/README.md
95
+ - lib/nse_data/cache/cache_policy.rb
96
+ - lib/nse_data/cache/cache_store.rb
97
+ - lib/nse_data/cache/redis_cache_store.rb
98
+ - lib/nse_data/config/api_endpoints.yml
99
+ - lib/nse_data/config/base.rb
100
+ - lib/nse_data/config/logger.rb
101
+ - lib/nse_data/http_client/base_client.rb
102
+ - lib/nse_data/http_client/faraday_client.rb
103
+ - lib/nse_data/version.rb
104
+ - nse_data.gemspec
105
+ homepage: https://github.com/ahasunos/nse_data
106
+ licenses:
107
+ - MIT
108
+ metadata:
109
+ homepage_uri: https://github.com/ahasunos/nse_data
110
+ source_code_uri: https://github.com/ahasunos/nse_data
111
+ changelog_uri: https://github.com/ahasunos/nse_data/blob/main/CHANGELOG.md
112
+ post_install_message:
113
+ rdoc_options: []
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: 3.1.0
121
+ required_rubygems_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ requirements: []
127
+ rubygems_version: 3.4.10
128
+ signing_key:
129
+ specification_version: 4
130
+ summary: A Ruby gem for accessing NSE data
131
+ test_files: []