Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/expose methods via nse data module #36

Merged
merged 7 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,23 @@ Or install it yourself as:
gem install nse_data
```

## Available Methods

- fetch_all_indices
- fetch_circulars
- fetch_equity_master
- fetch_glossary
- fetch_holiday_clearing
- fetch_holiday_trading
- fetch_index_names
- fetch_latest_circulars
- fetch_market_data_pre_open
- fetch_market_status
- fetch_market_turnover
- fetch_merged_daily_reports_capital
- fetch_merged_daily_reports_debt
- fetch_merged_daily_reports_derivatives

## Usage

### High-Level Interface
Expand All @@ -54,8 +71,8 @@ The high-level interface is designed to be simple and user-friendly. You can ins
```ruby
require 'nse_data'

apis = NseData.list_all_endpoints
puts apis
puts NseData.list_all_endpoints # Returns all the available APIs in the library
puts NseData.fetch_all_indices # Returns the response.body of allIndices endpoint of NSE
```

### Low-Level API
Expand Down
41 changes: 41 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,49 @@ YARD::Rake::YardocTask.new
# By default, run both the `spec` (tests) and `rubocop` (linter) tasks
task default: %i[spec rubocop]

require_relative 'lib/nse_data'

namespace :docs do
desc 'Update README with dynamically defined methods'
task :update_readme do
# Ensure the API methods are defined
NseData.define_api_methods

# List all methods defined on NseData
methods = NseData.methods(false).grep(/^fetch_/)

# Read the existing README
readme_path = 'README.md'
readme_content = File.read(readme_path)

# Define markers for where to insert the methods section
start_marker = '## Available Methods'
end_marker = '## Usage'
start_index = readme_content.index(start_marker) + start_marker.length
end_index = readme_content.index(end_marker)

if start_index && end_index
methods_section = readme_content[start_index...end_index]
new_methods_section = "#{start_marker}\n\n"
methods.each do |method|
new_methods_section += "- #{method}\n"
end

# Write back the updated README
updated_content = readme_content.sub(methods_section, new_methods_section)
File.write(readme_path, updated_content)

puts 'README updated with the following methods:'
methods.each { |method| puts "- #{method}" }
else
puts 'Could not find markers to update in README.md'
end
end
end

# Usage:
# 1. `rake spec` - Runs the RSpec tests
# 2. `rake rubocop` - Runs RuboCop linter for code quality checks
# 3. `rake yard` - Generates Yard documentation (output in the `doc/` folder)
# 4. `rake` - Runs both the tests and RuboCop checks (default task)
# 5. `rake docs:update_readme` - Update readme with available methods generated dynamically in nse_data.rb
12 changes: 11 additions & 1 deletion lib/nse_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,21 @@ module NseData
class Error < StandardError; end

# This module provides functionality for accessing NSE data.
def self.define_api_methods
api_manager = APIManager.new
api_manager.endpoints.each_key do |method_name|
define_singleton_method("fetch_#{method_name}") do
api_manager.fetch_data(method_name).body
end
end
end

# Returns a list of all available endpoints.
#
# @return [Array] An array of endpoint names.
def self.list_all_endpoints
@list_all_endpoints ||= NseData::APIManager.new.load_endpoints
@list_all_endpoints ||= APIManager.new.load_endpoints
end
end

NseData.define_api_methods
13 changes: 2 additions & 11 deletions lib/nse_data/api_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ class APIManager
def initialize
@client = NseData::HttpClient::FaradayClient.new(BASE_URL)
@endpoints = load_endpoints
define_api_methods
end

# Fetches data from the specified API endpoint.
Expand All @@ -35,15 +34,7 @@ def load_endpoints
yaml_content['apis']
end

private

# Defines dynamic API methods based on the loaded endpoints.
def define_api_methods
@endpoints.each_key do |method_name|
self.class.define_method("get_#{method_name}") do
fetch_data(method_name)
end
end
end
# @return [Hash] The hash containing the loaded API endpoints.
attr_reader :endpoints
end
end
18 changes: 0 additions & 18 deletions spec/nse_data/api_manager_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,6 @@
allow(NseData::HttpClient::FaradayClient).to receive(:new).with('https://www.nseindia.com/api/').and_return(faraday_client)
end

context 'when object is initialized' do
describe '#initialize' do
it 'initializes with FaradayClient and loads endpoints' do
manager = described_class.new

expect(manager.instance_variable_get(:@client)).to eq(faraday_client)
expect(manager.instance_variable_get(:@endpoints)).to eq(api_endpoints)
end

it 'defines methods for each endpoint' do
manager = described_class.new

expect(manager).to respond_to(:get_endpoint1)
expect(manager).to respond_to(:get_endpoint2)
end
end
end

describe '#fetch_data' do
before do
allow(faraday_client).to receive(:get).with('/api/endpoint1').and_return('data1')
Expand Down
38 changes: 28 additions & 10 deletions spec/nse_data_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,42 @@
require 'nse_data/api_manager'

RSpec.describe NseData do
let(:api_endpoints) do
let(:api_manager) { instance_double('NseData::APIManager') }
let(:endpoints) do
{
'endpoint1' => { 'path' => '/api/endpoint1' },
'endpoint2' => { 'path' => '/api/endpoint2' }
'market_data' => { 'path' => '/market/data' },
'stock_summary' => { 'path' => '/stock/summary' }
}
end

before do
# Mock the APIManager to control its behavior
api_manager_double = instance_double('NseData::APIManager')
allow(api_manager_double).to receive(:load_endpoints).and_return(api_endpoints)
allow(NseData::APIManager).to receive(:new).and_return(api_manager_double)
allow(NseData::APIManager).to receive(:new).and_return(api_manager)
allow(api_manager).to receive(:load_endpoints).and_return(endpoints)
allow(api_manager).to receive(:fetch_data).and_return(double('Faraday::Response', body: '{}'))
allow(api_manager).to receive(:endpoints).and_return(endpoints)
NseData.define_api_methods
end

describe '.define_api_methods' do
it 'dynamically defines methods for each endpoint' do
expect(NseData).to respond_to(:fetch_market_data)
expect(NseData).to respond_to(:fetch_stock_summary)
end

it 'calls the APIManager to fetch data when method is invoked' do
NseData.fetch_market_data
expect(api_manager).to have_received(:fetch_data).with('market_data')

NseData.fetch_stock_summary
expect(api_manager).to have_received(:fetch_data).with('stock_summary')
end
end

describe '.list_all_endpoints' do
it 'returns all endpoints loaded by APIManager' do
endpoints = NseData.list_all_endpoints
expect(endpoints).to eq(api_endpoints)
it 'returns a list of all available endpoints' do
expected_endpoints = { 'market_data' => { 'path' => '/market/data' },
'stock_summary' => { 'path' => '/stock/summary' } }
expect(NseData.list_all_endpoints).to eq(expected_endpoints)
end
end
end
Loading