diff --git a/examples/stories/movie_lister/_create_db.py b/examples/stories/movie_lister/_create_db.py new file mode 100644 index 00000000..7d16a336 --- /dev/null +++ b/examples/stories/movie_lister/_create_db.py @@ -0,0 +1,33 @@ +"""Small script for initializing movies data.""" + +import os +import csv +import sqlite3 +import shutil + +from settings import DATA_DIR +from settings import MOVIES_CSV_PATH +from settings import MOVIES_DB_PATH + + +MOVIES = (('The Hunger Games: Mockingjay - Part 2', 2015, 'Francis Lawrence'), + ('The 33', 2015, 'Patricia Riggen'), + ('Star Wars: Episode VII - The Force Awakens', 2015, 'JJ Abrams')) + + +if __name__ == '__main__': + # (Re)create data directory: + if os.path.exists(DATA_DIR): + shutil.rmtree(DATA_DIR) + os.makedirs(DATA_DIR) + + # Initialize sqlite database: + connection = sqlite3.connect(MOVIES_DB_PATH) + with connection: + connection.execute('CREATE TABLE movies ' + '(name text, year int, director text)') + connection.executemany('INSERT INTO movies VALUES (?,?,?)', MOVIES) + + # Initialize csv database: + with open(MOVIES_CSV_PATH, 'w') as csv_file: + csv.writer(csv_file).writerows(MOVIES) diff --git a/examples/stories/movie_lister/app_csv.py b/examples/stories/movie_lister/app_csv.py new file mode 100644 index 00000000..b444e105 --- /dev/null +++ b/examples/stories/movie_lister/app_csv.py @@ -0,0 +1,39 @@ +"""A naive example of dependency injection in Python. + +Example implementation of dependency injection in Python from Martin Fowler's +article about dependency injection and inversion of control. + +http://www.martinfowler.com/articles/injection.html +""" + +from movies.module import MoviesModule +from movies.components import CsvMovieFinder + +from settings import MOVIES_CSV_PATH + +from dependency_injector import catalogs +from dependency_injector import providers + + +@catalogs.override(MoviesModule) +class MyMoviesModule(catalogs.DeclarativeCatalog): + """Customized catalog of movie module components.""" + + movie_finder = providers.Factory(CsvMovieFinder, + csv_file=MOVIES_CSV_PATH, + delimeter=',') + + +def main(): + """Main function.""" + movie_lister = MoviesModule.movie_lister() + + print movie_lister.movies_directed_by('Francis Lawrence') + print movie_lister.movies_directed_by('Patricia Riggen') + print movie_lister.movies_directed_by('JJ Abrams') + + print movie_lister.movies_released_in(2015) + + +if __name__ == '__main__': + main() diff --git a/examples/stories/movie_lister/app_db.py b/examples/stories/movie_lister/app_db.py new file mode 100644 index 00000000..d6f78ada --- /dev/null +++ b/examples/stories/movie_lister/app_db.py @@ -0,0 +1,48 @@ +"""A naive example of dependency injection in Python. + +Example implementation of dependency injection in Python from Martin Fowler's +article about dependency injection and inversion of control. + +http://www.martinfowler.com/articles/injection.html +""" + +import sqlite3 + +from movies.module import MoviesModule +from movies.components import SqliteMovieFinder + +from settings import MOVIES_DB_PATH + +from dependency_injector import catalogs +from dependency_injector import providers + + +class ApplicationModule(catalogs.DeclarativeCatalog): + """Catalog of application components.""" + + database = providers.Singleton(sqlite3.connect, + MOVIES_DB_PATH) + """:type: providers.Provider -> components.MovieFinder""" + + +@catalogs.override(MoviesModule) +class MyMoviesModule(catalogs.DeclarativeCatalog): + """Customized catalog of movie module components.""" + + movie_finder = providers.Factory(SqliteMovieFinder, + database=ApplicationModule.database) + + +def main(): + """Main function.""" + movie_lister = MoviesModule.movie_lister() + + print movie_lister.movies_directed_by('Francis Lawrence') + print movie_lister.movies_directed_by('Patricia Riggen') + print movie_lister.movies_directed_by('JJ Abrams') + + print movie_lister.movies_released_in(2015) + + +if __name__ == '__main__': + main() diff --git a/examples/stories/movie_lister/data/.gitignore b/examples/stories/movie_lister/data/.gitignore new file mode 100644 index 00000000..1bcf41d7 --- /dev/null +++ b/examples/stories/movie_lister/data/.gitignore @@ -0,0 +1 @@ +movies.* diff --git a/examples/stories/movie_lister/movies/__init__.py b/examples/stories/movie_lister/movies/__init__.py new file mode 100644 index 00000000..29dd5aaa --- /dev/null +++ b/examples/stories/movie_lister/movies/__init__.py @@ -0,0 +1 @@ +"""Movies package.""" diff --git a/examples/stories/movie_lister/movies/components.py b/examples/stories/movie_lister/movies/components.py new file mode 100644 index 00000000..14ae4fdd --- /dev/null +++ b/examples/stories/movie_lister/movies/components.py @@ -0,0 +1,100 @@ +"""Movies package components.""" + +import csv + + +class MovieLister(object): + """Movie lister.""" + + def __init__(self, movie_finder): + """Initializer.""" + self.movie_finder = movie_finder + + def movies_directed_by(self, director): + """Return list of movies that were directed by certain person. + + :param director: Director's name + :type director: str + + :rtype: list[:py:class:`Movie`] + :return: List of movie instances. + """ + return [movie for movie in self.movie_finder.find_all() + if movie.director == director] + + def movies_released_in(self, year): + """Return list of movies that were released in certain year. + + :param year: Release year + :type year: int + + :rtype: list[:py:class:`Movie`] + :return: List of movie instances. + """ + return [movie for movie in self.movie_finder.find_all() + if movie.year == year] + + +class MovieFinder(object): + """Movie finder.""" + + def find_all(self): + """Return all found movies. + + :rtype: list[:py:class:`Movie`] + :return: List of movie instances. + """ + raise NotImplementedError() + + +class CsvMovieFinder(MovieFinder): + """Movie finder that fetches movies info from csv file.""" + + def __init__(self, csv_file, delimeter): + """Initializer.""" + self.csv_file = csv_file + self.delimeter = delimeter + + def find_all(self): + """Return all found movies. + + :rtype: list[:py:class:`Movie`] + :return: List of movie instances. + """ + with open(self.csv_file) as csv_file: + reader = csv.reader(csv_file, delimiter=self.delimeter) + return [Movie(*row) for row in reader] + + +class SqliteMovieFinder(MovieFinder): + """Movie finder that fetches movies info from sqlite database.""" + + def __init__(self, database): + """Initializer.""" + self.database = database + + def find_all(self): + """Return all found movies. + + :rtype: list[:py:class:`Movie`] + :return: List of movie instances. + """ + with self.database: + rows = self.database.execute('SELECT name, year, director ' + 'FROM movies') + return [Movie(*row) for row in rows] + + +class Movie(object): + """Movie model.""" + + def __init__(self, name, year, director): + """Initializer.""" + self.name = str(name) + self.year = int(year) + self.director = str(director) + + def __repr__(self): + """Return string representation of movie instance.""" + return 'Movie(name={0}, year={1}, director={2})'.format( + repr(self.name), repr(self.year), repr(self.director)) diff --git a/examples/stories/movie_lister/movies/module.py b/examples/stories/movie_lister/movies/module.py new file mode 100644 index 00000000..b0e4ad58 --- /dev/null +++ b/examples/stories/movie_lister/movies/module.py @@ -0,0 +1,17 @@ +"""Movies package dependency injection module.""" + +from . import components + +from dependency_injector import catalogs +from dependency_injector import providers + + +class MoviesModule(catalogs.DeclarativeCatalog): + """Catalog of movie module components.""" + + movie_finder = providers.Factory(components.MovieFinder) + """:type: providers.Provider -> components.MovieFinder""" + + movie_lister = providers.Factory(components.MovieLister, + movie_finder=movie_finder) + """:type: providers.Provider -> components.MovieLister""" diff --git a/examples/stories/movie_lister/settings.py b/examples/stories/movie_lister/settings.py new file mode 100644 index 00000000..f318b9e1 --- /dev/null +++ b/examples/stories/movie_lister/settings.py @@ -0,0 +1,14 @@ +"""Settings module. + +This module contains application's settings and constants. +""" + +import os + + +DATA_DIR = os.path.abspath(os.path.dirname(__file__) + '/data') + +MOVIES_CSV_PATH = DATA_DIR + '/movies.csv' +MOVIES_CSV_DELIMETER = ',' + +MOVIES_DB_PATH = DATA_DIR + '/movies.db'